//: 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:literal 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:literal ] # 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") Recipe_ordinal["wait-for-location"] = WAIT_FOR_LOCATION; :(before "End Primitive Recipe Implementations") case WAIT_FOR_LOCATION: { reagent loc = canonize(current_instruction().ingredients.at(0)); Current_routine->state = WAITING; Current_routine->waiting_on_location = loc.value; Current_routine->old_value_of_waiting_location = Memory[loc.value]; trace(Primitive_recipe_depth, "run") << "waiting for location " << loc.value << " to change from " << Memory[loc.value]; //? trace("schedule") << Current_routine->id << ": waiting for location " << loc.value << " to change from " << Memory[loc.value]; //? 2 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) { //? trace("schedule") << "wake up loop 1: routine " << Routines.at(i)->id << " has state " << Routines.at(i)->state; //? 1 if (Routines.at(i)->state != WAITING) continue; //? trace("schedule") << "waiting on location: " << Routines.at(i)->waiting_on_location; //? 1 //? if (Routines.at(i)->waiting_on_location) //? 2 //? trace("schedule") << "checking routine " << Routines.at(i)->id << " waiting on location " //? 2 //? << Routines.at(i)->waiting_on_location << ": " << Memory[Routines.at(i)->waiting_on_location] << " vs " << Routines.at(i)->old_value_of_waiting_location; //? 2 if (Routines.at(i)->waiting_on_location && Memory[Routines.at(i)->waiting_on_location] != Routines.at(i)->old_value_of_waiting_location) { trace("schedule") << "waking up routine\n"; 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:literal 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:literal ] +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") Recipe_ordinal["wait-for-routine"] = WAIT_FOR_ROUTINE; :(before "End Primitive Recipe Implementations") case WAIT_FOR_ROUTINE: { Current_routine->state = WAITING; assert(scalar(ingredients.at(0))); Current_routine->waiting_on_routine = ingredients.at(0).at(0); trace(Primitive_recipe_depth, "run") << "waiting for routine " << ingredients.at(0).at(0); 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); for (long long int j = 0; j < SIZE(Routines); ++j) { if (Routines.at(j)->id == id && Routines.at(j)->state != RUNNING) { trace("schedule") << "waking up routine " << Routines.at(i)->id; Routines.at(i)->state = RUNNING; Routines.at(i)->waiting_on_routine = 0; } } } :(before "End Primitive Recipe Declarations") SWITCH, :(before "End Primitive Recipe Numbers") Recipe_ordinal["switch"] = SWITCH; :(before "End Primitive Recipe Implementations") case SWITCH: { long long int id = some_other_running_routine(); if (id) { assert(id != Current_routine->id); //? cerr << "waiting on " << id << " from " << Current_routine->id << '\n'; //? 1 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; }