//: Run a second routine concurrently using 'start-running', without any //: guarantees on how the operations in each are interleaved with each other. :(scenario scheduler) recipe f1 [ start-running f2:recipe # wait for f2 to run { jump-unless 1:number, -1 } ] recipe f2 [ 1:number <- copy 1 ] +schedule: f1 +schedule: f2 //: first, add a deadline to run(routine) //: these changes are ugly and brittle; just close your nose and get through the next few lines :(replace "void run_current_routine()") void run_current_routine(long long int time_slice) :(replace "while (!Current_routine->completed())" following "void run_current_routine(long long int time_slice)") long long int ninstrs = 0; while (Current_routine->state == RUNNING && ninstrs < time_slice) :(after "Running One Instruction") ninstrs++; //: now the rest of the scheduler is clean :(before "struct routine") enum routine_state { RUNNING, COMPLETED, // End routine States }; :(before "End routine Fields") enum routine_state state; :(before "End routine Constructor") state = RUNNING; :(before "End Globals") vector Routines; long long int Current_routine_index = 0; long long int Scheduling_interval = 500; :(before "End Setup") Scheduling_interval = 500; Routines.clear(); :(replace{} "void run(recipe_ordinal r)") void run(recipe_ordinal r) { Routines.push_back(new routine(r)); Current_routine_index = 0, Current_routine = Routines.at(0); while (!all_routines_done()) { skip_to_next_routine(); assert(Current_routine); assert(Current_routine->state == RUNNING); trace("schedule") << current_routine_label() << end(); run_current_routine(Scheduling_interval); // Scheduler State Transitions if (Current_routine->completed()) Current_routine->state = COMPLETED; // End Scheduler State Transitions // Scheduler Cleanup // End Scheduler Cleanup } } :(code) bool all_routines_done() { for (long long int i = 0; i < SIZE(Routines); ++i) { if (Routines.at(i)->state == RUNNING) { return false; } } return true; } // skip Current_routine_index past non-RUNNING routines void skip_to_next_routine() { assert(!Routines.empty()); assert(Current_routine_index < SIZE(Routines)); for (long long int i = (Current_routine_index+1)%SIZE(Routines); i != Current_routine_index; i = (i+1)%SIZE(Routines)) { if (Routines.at(i)->state == RUNNING) { Current_routine_index = i; Current_routine = Routines.at(i); return; } } } string current_routine_label() { ostringstream result; call_stack calls = Current_routine->calls; for (call_stack::iterator p = calls.begin(); p != calls.end(); ++p) { if (p != calls.begin()) result << '/'; result << Recipe[p->running_recipe].name; } return result.str(); } :(before "End Teardown") for (long long int i = 0; i < SIZE(Routines); ++i) delete Routines.at(i); Routines.clear(); //:: To schedule new routines to run, call 'start-running'. //: 'start-running' will return a unique id for the routine that was created. //: routine id is a number, but don't do any arithmetic on it :(before "End routine Fields") long long int id; :(before "End Globals") long long int Next_routine_id = 1; :(before "End Setup") Next_routine_id = 1; :(before "End routine Constructor") id = Next_routine_id; Next_routine_id++; //: routines save the routine that spawned them :(before "End routine Fields") // todo: really should be routine_id, but that's less efficient. long long int parent_index; // only < 0 if there's no parent_index :(before "End routine Constructor") parent_index = -1; :(before "End Primitive Recipe Declarations") START_RUNNING, :(before "End Primitive Recipe Numbers") Recipe_ordinal["start-running"] = START_RUNNING; :(before "End Primitive Recipe Implementations") case START_RUNNING: { if (ingredients.empty()) { raise << "'start-running' requires at least one ingredient: the recipe to start running\n" << end(); break; } if (!scalar(ingredients.at(0))) { raise << "first ingredient of 'start-running' should be a recipe, but got " << current_instruction().ingredients.at(0).original_string << '\n' << end(); break; } if (!ingredients.at(0).at(0)) { raise << "'start-running' received non-existent recipe: '" << current_instruction().to_string() <
@import url("https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/dark.min.css");

#container {
  max-width: 50rem;
  margin: auto;
  margin-top: -2rem;
}

#head {
  padding-bottom: 1rem;
  text-align: center;
  font-size: 4em;
  font-family: monospace;
}

#subhead {
  text-align: right;
  font-size: 2.0em;
  font-family: monospace;
  margin-top: -2rem;
  padding-right: 0rem;
  padding-bottom: 0rem;
}

#body {
  font-size: 1.0em;
}

#info {
  font-family: monospace;
  font-size: 1.5