about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--073wait.cc67
-rw-r--r--075channel.mu5
2 files changed, 65 insertions, 7 deletions
diff --git a/073wait.cc b/073wait.cc
index 6f1ba14c..8db03834 100644
--- a/073wait.cc
+++ b/073wait.cc
@@ -363,8 +363,63 @@ for (int i = 0; i < SIZE(Routines); ++i) {
   }
 }
 
+//:: helpers for manipulating routines in tests
+//:
+//: Managing arbitrary scenarios requires the ability to:
+//:   a) stop the current routine (`switch`)
+//:   b) restart a routine (`restart`)
+//:   c) tell when a routine is blocked
+//:
+//: A routine is blocked either if it's waiting or if it explicitly signals
+//: that it's blocked (even as it periodically wakes up and polls for some
+//: event).
+//:
+//: Signalling blockedness might well be a huge hack. But Mu doesn't have Unix
+//: signals to avoid polling with, because signals are also pretty hacky.
+
+:(before "End routine Fields")
+bool blocked;
+:(before "End routine Constructor")
+blocked = false;
+
+:(before "End Primitive Recipe Declarations")
+CURRENT_ROUTINE_IS_BLOCKED,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "current-routine-is-blocked", CURRENT_ROUTINE_IS_BLOCKED);
+:(before "End Primitive Recipe Checks")
+case CURRENT_ROUTINE_IS_BLOCKED: {
+  if (!inst.ingredients.empty()) {
+    raise << maybe(get(Recipe, r).name) << "'current-routine-is-blocked' should have no ingredients, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case CURRENT_ROUTINE_IS_BLOCKED: {
+  Current_routine->blocked = true;
+  break;
+}
+
+:(before "End Primitive Recipe Declarations")
+CURRENT_ROUTINE_IS_UNBLOCKED,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "current-routine-is-unblocked", CURRENT_ROUTINE_IS_UNBLOCKED);
+:(before "End Primitive Recipe Checks")
+case CURRENT_ROUTINE_IS_UNBLOCKED: {
+  if (!inst.ingredients.empty()) {
+    raise << maybe(get(Recipe, r).name) << "'current-routine-is-unblocked' should have no ingredients, but got '" << inst.original_string << "'\n" << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case CURRENT_ROUTINE_IS_UNBLOCKED: {
+  Current_routine->blocked = false;
+  break;
+}
+
 //: also allow waiting on a routine to block
-//: (just for tests; use wait_for_routine below wherever possible)
+//: (just for tests; use wait_for_routine above wherever possible)
 
 :(scenario wait_for_routine_to_block)
 def f1 [
@@ -379,7 +434,7 @@ def f2 [
 +schedule: f1
 +run: waiting for routine 2 to block
 +schedule: f2
-+schedule: waking up blocked routine 1
++schedule: waking up routine 1 because routine 2 is blocked
 +schedule: f1
 # if we got the synchronization wrong we'd be storing 0 in location 11
 +mem: storing 34 in location 11
@@ -415,7 +470,6 @@ case WAIT_FOR_ROUTINE_TO_BLOCK: {
   Current_routine->state = WAITING;
   Current_routine->waiting_on_routine_to_block = ingredients.at(0).at(0);
   trace(9998, "run") << "waiting for routine " << ingredients.at(0).at(0) << " to block" << end();
-//?   cerr << Current_routine->id << ": waiting for routine " << ingredients.at(0).at(0) << " to block\n";
   break;
 }
 
@@ -429,10 +483,9 @@ for (int i = 0; i < SIZE(Routines); ++i) {
   assert(id != waiter->id);  // routine can't wait on itself
   for (int j = 0; j < SIZE(Routines); ++j) {
     const routine* waitee = Routines.at(j);
-    if (waitee->id == id && waitee->state != RUNNING) {
-      // routine is WAITING or COMPLETED or DISCONTINUED
-      trace(9999, "schedule") << "waking up blocked routine " << waiter->id << end();
-//?       cerr << id << " is now unblocked (" << waitee->state << "); waking up waiting routine " << waiter->id << '\n';
+    if (waitee->id != id) continue;
+    if (waitee->state != RUNNING || waitee->blocked) {
+      trace(9999, "schedule") << "waking up routine " << waiter->id << " because routine " << waitee->id << " is blocked" << end();
       waiter->state = RUNNING;
       waiter->waiting_on_routine_to_block = 0;
     }
diff --git a/075channel.mu b/075channel.mu
index 7720ccdd..49ebad87 100644
--- a/075channel.mu
+++ b/075channel.mu
@@ -78,9 +78,11 @@ def write out:address:sink:_elem, val:_elem -> out:address:sink:_elem [
     # channel is full; relinquish lock and give a reader the opportunity to
     # create room on it
     reset lock
+    current-routine-is-blocked
     switch  # avoid spinlocking
     loop
   }
+  current-routine-is-unblocked
 #?   $print [performing write], 10/newline
   # store a deep copy of val
   circular-buffer:address:array:_elem <- get *chan, data:offset
@@ -121,10 +123,12 @@ def read in:address:source:_elem -> result:_elem, eof?:boolean, in:address:sourc
     # channel is empty; relinquish lock and give a writer the opportunity to
     # add to it
     reset lock
+    current-routine-is-blocked
     <channel-read-empty>
     switch  # avoid spinlocking
     loop
   }
+  current-routine-is-unblocked
   # pull result off
   full:number <- get *chan, first-full:offset
   circular-buffer:address:array:_elem <- get *chan, data:offset
@@ -327,6 +331,7 @@ after <channel-read-empty> [
   {
     break-unless closed?
     empty-result:address:_elem <- new _elem:type
+    current-routine-is-unblocked
     return *empty-result, 1/true
   }
 ]