about summary refs log tree commit diff stats
path: root/063wait.cc
diff options
context:
space:
mode:
Diffstat (limited to '063wait.cc')
-rw-r--r--063wait.cc167
1 files changed, 167 insertions, 0 deletions
diff --git a/063wait.cc b/063wait.cc
new file mode 100644
index 00000000..ef3d30e5
--- /dev/null
+++ b/063wait.cc
@@ -0,0 +1,167 @@
+//: Routines can be put in a 'waiting' state, from which it will be ready to
+//: run again when a specific memory location changes its value. This is mu's
+//: basic technique for orchestrating the order in which different routines
+//: operate.
+
+:(scenario wait_for_location)
+recipe f1 [
+  1:number <- copy 0
+  start-running f2:recipe
+  wait-for-location 1:number
+  # now wait for f2 to run and modify location 1 before using its value
+  2:number <- copy 1:number
+]
+recipe f2 [
+  1:number <- copy 34
+]
+# if we got the synchronization wrong we'd be storing 0 in location 2
++mem: storing 34 in location 2
+
+//: define the new state that all routines can be in
+
+:(before "End routine States")
+WAITING,
+:(before "End routine Fields")
+// only if state == WAITING
+long long int waiting_on_location;
+int old_value_of_waiting_location;
+:(before "End routine Constructor")
+waiting_on_location = old_value_of_waiting_location = 0;
+
+//: primitive recipe to put routines in that state
+
+:(before "End Primitive Recipe Declarations")
+WAIT_FOR_LOCATION,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "wait-for-location", WAIT_FOR_LOCATION);
+:(before "End Primitive Recipe Checks")
+case WAIT_FOR_LOCATION: {
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case WAIT_FOR_LOCATION: {
+  reagent loc = current_instruction().ingredients.at(0);
+  canonize(loc);
+  Current_routine->state = WAITING;
+  Current_routine->waiting_on_location = loc.value;
+  Current_routine->old_value_of_waiting_location = get_or_insert(Memory, loc.value);
+  trace(9998, "run") << "waiting for location " << loc.value << " to change from " << no_scientific(get_or_insert(Memory, loc.value)) << end();
+  break;
+}
+
+//: scheduler tweak to get routines out of that state
+
+:(before "End Scheduler State Transitions")
+for (long long int i = 0; i < SIZE(Routines); ++i) {
+  if (Routines.at(i)->state != WAITING) continue;
+  if (Routines.at(i)->waiting_on_location &&
+      get_or_insert(Memory, Routines.at(i)->waiting_on_location) != Routines.at(i)->old_value_of_waiting_location) {
+    trace(9999, "schedule") << "waking up routine\n" << end();
+    Routines.at(i)->state = RUNNING;
+    Routines.at(i)->waiting_on_location = Routines.at(i)->old_value_of_waiting_location = 0;
+  }
+}
+
+//: also allow waiting on a routine to stop running
+
+:(scenario wait_for_routine)
+recipe f1 [
+  1:number <- copy 0
+  12:number/routine <- start-running f2:recipe
+  wait-for-routine 12:number/routine
+  # now wait for f2 to run and modify location 1 before using its value
+  3:number <- copy 1:number
+]
+recipe f2 [
+  1:number <- copy 34
+]
++schedule: f1
++run: waiting for routine 2
++schedule: f2
++schedule: waking up routine 1
++schedule: f1
+# if we got the synchronization wrong we'd be storing 0 in location 3
++mem: storing 34 in location 3
+
+:(before "End routine Fields")
+// only if state == WAITING
+long long int waiting_on_routine;
+:(before "End routine Constructor")
+waiting_on_routine = 0;
+
+:(before "End Primitive Recipe Declarations")
+WAIT_FOR_ROUTINE,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "wait-for-routine", WAIT_FOR_ROUTINE);
+:(before "End Primitive Recipe Checks")
+case WAIT_FOR_ROUTINE: {
+  if (SIZE(inst.ingredients) != 1) {
+    raise_error << maybe(get(Recipe, r).name) << "'wait-for-routine' requires exactly one ingredient, but got " << inst.to_string() << '\n' << end();
+    break;
+  }
+  if (!is_mu_number(inst.ingredients.at(0))) {
+    raise_error << maybe(get(Recipe, r).name) << "first ingredient of 'wait-for-routine' should be a routine id generated by 'start-running', but got " << inst.ingredients.at(0).original_string << '\n' << end();
+    break;
+  }
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case WAIT_FOR_ROUTINE: {
+  if (ingredients.at(0).at(0) == Current_routine->id) {
+    raise_error << maybe(current_recipe_name()) << "routine can't wait for itself! " << current_instruction().to_string() << '\n' << end();
+    break;
+  }
+  Current_routine->state = WAITING;
+  Current_routine->waiting_on_routine = ingredients.at(0).at(0);
+  trace(9998, "run") << "waiting for routine " << ingredients.at(0).at(0) << end();
+  break;
+}
+
+:(before "End Scheduler State Transitions")
+// Wake up any routines waiting for other routines to go to sleep.
+// Important: this must come after the scheduler loop above giving routines
+// waiting for locations to change a chance to wake up.
+for (long long int i = 0; i < SIZE(Routines); ++i) {
+  if (Routines.at(i)->state != WAITING) continue;
+  if (!Routines.at(i)->waiting_on_routine) continue;
+  long long int id = Routines.at(i)->waiting_on_routine;
+  assert(id != Routines.at(i)->id);  // routine can't wait on itself
+  for (long long int j = 0; j < SIZE(Routines); ++j) {
+    if (Routines.at(j)->id == id && Routines.at(j)->state != RUNNING) {
+      trace(9999, "schedule") << "waking up routine " << Routines.at(i)->id << end();
+      Routines.at(i)->state = RUNNING;
+      Routines.at(i)->waiting_on_routine = 0;
+    }
+  }
+}
+
+:(before "End Primitive Recipe Declarations")
+SWITCH,
+:(before "End Primitive Recipe Numbers")
+put(Recipe_ordinal, "switch", SWITCH);
+:(before "End Primitive Recipe Checks")
+case SWITCH: {
+  break;
+}
+:(before "End Primitive Recipe Implementations")
+case SWITCH: {
+  long long int id = some_other_running_routine();
+  if (id) {
+    assert(id != Current_routine->id);
+    Current_routine->state = WAITING;
+    Current_routine->waiting_on_routine = id;
+  }
+  break;
+}
+
+:(code)
+long long int some_other_running_routine() {
+  for (long long int i = 0; i < SIZE(Routines); ++i) {
+    if (i == Current_routine_index) continue;
+    assert(Routines.at(i) != Current_routine);
+    assert(Routines.at(i)->id != Current_routine->id);
+    if (Routines.at(i)->state == RUNNING)
+      return Routines.at(i)->id;
+  }
+  return 0;
+}