//: 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 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 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_r