From 81a324bec3e992d6a979830c5bcae89ac7cdf40c Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Wed, 13 Sep 2017 21:11:24 -0700 Subject: 3995 --- html/072scheduler.cc.html | 775 ---------------------------------------------- 1 file changed, 775 deletions(-) delete mode 100644 html/072scheduler.cc.html (limited to 'html/072scheduler.cc.html') diff --git a/html/072scheduler.cc.html b/html/072scheduler.cc.html deleted file mode 100644 index 812f8691..00000000 --- a/html/072scheduler.cc.html +++ /dev/null @@ -1,775 +0,0 @@ - - - - -Mu - 072scheduler.cc - - - - - - - - - - -
-  1 //: Run a second routine concurrently using 'start-running', without any
-  2 //: guarantees on how the operations in each are interleaved with each other.
-  3 
-  4 :(scenario scheduler)
-  5 def f1 [
-  6   start-running f2
-  7   # wait for f2 to run
-  8   {
-  9   ¦ jump-unless 1:num, -1
- 10   }
- 11 ]
- 12 def f2 [
- 13   1:num <- copy 1
- 14 ]
- 15 +schedule: f1
- 16 +schedule: f2
- 17 
- 18 //: first, add a deadline to run(routine)
- 19 :(before "End Globals")
- 20 int Scheduling_interval = 500;
- 21 :(before "End routine Fields")
- 22 int instructions_run_this_scheduling_slice;
- 23 :(before "End routine Constructor")
- 24 instructions_run_this_scheduling_slice = 0;
- 25 :(after "Running One Instruction")
- 26  ++Current_routine->instructions_run_this_scheduling_slice;
- 27 :(replace{} "bool should_continue_running(const routine* current_routine)")
- 28 bool should_continue_running(const routine* current_routine) {
- 29   assert(current_routine == Current_routine);  // argument passed in just to make caller readable above
- 30   return Current_routine->state == RUNNING
- 31   ¦ ¦ && Current_routine->instructions_run_this_scheduling_slice < Scheduling_interval;
- 32 }
- 33 :(after "stop_running_current_routine:")
- 34 // Reset instructions_run_this_scheduling_slice
- 35 Current_routine->instructions_run_this_scheduling_slice = 0;
- 36 
- 37 //: now the rest of the scheduler is clean
- 38 
- 39 :(before "struct routine")
- 40 enum routine_state {
- 41   RUNNING,
- 42   COMPLETED,
- 43   // End routine States
- 44 };
- 45 :(before "End routine Fields")
- 46 enum routine_state state;
- 47 :(before "End routine Constructor")
- 48 state = RUNNING;
- 49 
- 50 :(before "End Globals")
- 51 vector<routine*> Routines;
- 52 int Current_routine_index = 0;
- 53 :(before "End Reset")
- 54 Scheduling_interval = 500;
- 55 for (int i = 0;  i < SIZE(Routines);  ++i)
- 56   delete Routines.at(i);
- 57 Routines.clear();
- 58 Current_routine = NULL;
- 59 :(replace{} "void run(const recipe_ordinal r)")
- 60 void run(const recipe_ordinal r) {
- 61   run(new routine(r));
- 62 }
- 63 
- 64 :(code)
- 65 void run(routine* rr) {
- 66   Routines.push_back(rr);
- 67   Current_routine_index = 0, Current_routine = Routines.at(0);
- 68   while (!all_routines_done()) {
- 69   ¦ skip_to_next_routine();
- 70   ¦ assert(Current_routine);
- 71   ¦ assert(Current_routine->state == RUNNING);
- 72   ¦ trace(9990, "schedule") << current_routine_label() << end();
- 73   ¦ run_current_routine();
- 74   ¦ // Scheduler State Transitions
- 75   ¦ if (Current_routine->completed())
- 76   ¦ ¦ Current_routine->state = COMPLETED;
- 77   ¦ // End Scheduler State Transitions
- 78 
- 79   ¦ // Scheduler Cleanup
- 80   ¦ // End Scheduler Cleanup
- 81   }
- 82   // End Run Routine
- 83 }
- 84 
- 85 bool all_routines_done() {
- 86   for (int i = 0;  i < SIZE(Routines);  ++i) {
- 87   ¦ if (Routines.at(i)->state == RUNNING)
- 88   ¦ ¦ return false;
- 89   }
- 90   return true;
- 91 }
- 92 
- 93 // skip Current_routine_index past non-RUNNING routines
- 94 void skip_to_next_routine() {
- 95   assert(!Routines.empty());
- 96   assert(Current_routine_index < SIZE(Routines));
- 97   for (int i = (Current_routine_index+1)%SIZE(Routines);  i != Current_routine_index;  i = (i+1)%SIZE(Routines)) {
- 98   ¦ if (Routines.at(i)->state == RUNNING) {
- 99   ¦ ¦ Current_routine_index = i;
-100   ¦ ¦ Current_routine = Routines.at(i);
-101   ¦ ¦ return;
-102   ¦ }
-103   }
-104 }
-105 
-106 string current_routine_label() {
-107   return routine_label(Current_routine);
-108 }
-109 
-110 string routine_label(routine* r) {
-111   ostringstream result;
-112   const call_stack& calls = r->calls;
-113   for (call_stack::const_iterator p = calls.begin();  p != calls.end();  ++p) {
-114   ¦ if (p != calls.begin()) result << '/';
-115   ¦ result << get(Recipe, p->running_recipe).name;
-116   }
-117   return result.str();
-118 }
-119 
-120 //: special case for the very first routine
-121 :(replace{} "void run_main(int argc, char* argv[])")
-122 void run_main(int argc, char* argv[]) {
-123   recipe_ordinal r = get(Recipe_ordinal, "main");
-124   assert(r);
-125   routine* main_routine = new routine(r);
-126   // pass in commandline args as ingredients to main
-127   // todo: test this
-128   Current_routine = main_routine;
-129   for (int i = 1;  i < argc;  ++i) {
-130   ¦ vector<double> arg;
-131   ¦ arg.push_back(new_mu_text(argv[i]));
-132   ¦ assert(get(Memory, arg.back()) == 0);
-133   ¦ put(Memory, arg.back(), 1);  // update refcount
-134   ¦ current_call().ingredient_atoms.push_back(arg);
-135   }
-136   run(main_routine);
-137 }
-138 
-139 //:: To schedule new routines to run, call 'start-running'.
-140 
-141 //: 'start-running' will return a unique id for the routine that was created.
-142 //: routine id is a number, but don't do any arithmetic on it
-143 :(before "End routine Fields")
-144 int id;
-145 :(before "End Globals")
-146 int Next_routine_id = 1;
-147 :(before "End Reset")
-148 Next_routine_id = 1;
-149 :(before "End routine Constructor")
-150 id = Next_routine_id;
-151 ++Next_routine_id;
-152 
-153 //: routines save the routine that spawned them
-154 :(before "End routine Fields")
-155 // todo: really should be routine_id, but that's less efficient.
-156 int parent_index;  // only < 0 if there's no parent_index
-157 :(before "End routine Constructor")
-158 parent_index = -1;
-159 
-160 :(before "End Primitive Recipe Declarations")
-161 START_RUNNING,
-162 :(before "End Primitive Recipe Numbers")
-163 put(Recipe_ordinal, "start-running", START_RUNNING);
-164 :(before "End Primitive Recipe Checks")
-165 case START_RUNNING: {
-166   if (inst.ingredients.empty()) {
-167   ¦ raise << maybe(get(Recipe, r).name) << "'start-running' requires at least one ingredient: the recipe to start running\n" << end();
-168   ¦ break;
-169   }
-170   if (!is_mu_recipe(inst.ingredients.at(0))) {
-171   ¦ raise << maybe(get(Recipe, r).name) << "first ingredient of 'start-running' should be a recipe, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end();
-172   ¦ break;
-173   }
-174   break;
-175 }
-176 :(before "End Primitive Recipe Implementations")
-177 case START_RUNNING: {
-178   routine* new_routine = new routine(ingredients.at(0).at(0));
-179   new_routine->parent_index = Current_routine_index;
-180   // populate ingredients
-181   for (int i = 1;  i < SIZE(current_instruction().ingredients);  ++i) {
-182   ¦ new_routine->calls.front().ingredient_atoms.push_back(ingredients.at(i));
-183   ¦ reagent/*copy*/ ingredient = current_instruction().ingredients.at(i);
-184   ¦ canonize_type(ingredient);
-185   ¦ new_routine->calls.front().ingredients.push_back(ingredient);
-186   ¦ // End Populate start-running Ingredient
-187   }
-188   Routines.push_back(new_routine);
-189   products.resize(1);
-190   products.at(0).push_back(new_routine->id);
-191   break;
-192 }
-193 
-194 :(scenario scheduler_runs_single_routine)
-195 % Scheduling_interval = 1;
-196 def f1 [
-197   1:num <- copy 0
-198   2:num <- copy 0
-199 ]
-200 +schedule: f1
-201 +run: {1: "number"} <- copy {0: "literal"}
-202 +schedule: f1
-203 +run: {2: "number"} <- copy {0: "literal"}
-204 
-205 :(scenario scheduler_interleaves_routines)
-206 % Scheduling_interval = 1;
-207 def f1 [
-208   start-running f2
-209   1:num <- copy 0
-210   2:num <- copy 0
-211 ]
-212 def f2 [
-213   3:num <- copy 0
-214   4:num <- copy 0
-215 ]
-216 +schedule: f1
-217 +run: start-running {f2: "recipe-literal"}
-218 +schedule: f2
-219 +run: {3: "number"} <- copy {0: "literal"}
-220 +schedule: f1
-221 +run: {1: "number"} <- copy {0: "literal"}
-222 +schedule: f2
-223 +run: {4: "number"} <- copy {0: "literal"}
-224 +schedule: f1
-225 +run: {2: "number"} <- copy {0: "literal"}
-226 
-227 :(scenario start_running_takes_ingredients)
-228 def f1 [
-229   start-running f2, 3
-230   # wait for f2 to run
-231   {
-232   ¦ jump-unless 1:num, -1
-233   }
-234 ]
-235 def f2 [
-236   1:num <- next-ingredient
-237   2:num <- add 1:num, 1
-238 ]
-239 +mem: storing 4 in location 2
-240 
-241 //: type-checking for 'start-running'
-242 
-243 :(scenario start_running_checks_types)
-244 % Hide_errors = true;
-245 def f1 [
-246   start-running f2, 3
-247 ]
-248 def f2 n:&:num [
-249 ]
-250 +error: f1: ingredient 0 has the wrong type at 'start-running f2, 3'
-251 
-252 // 'start-running' only uses the ingredients of the callee, not its products
-253 :(before "End is_indirect_call_with_ingredients Special-cases")
-254 if (r == START_RUNNING) return true;
-255 
-256 //: more complex: refcounting management when starting up new routines
-257 
-258 :(scenario start_running_immediately_updates_refcounts_of_ingredients)
-259 % Scheduling_interval = 1;
-260 def main [
-261   local-scope
-262   create-new-routine
-263   # padding to make sure we run new-routine before returning
-264   dummy:num <- copy 0
-265   dummy:num <- copy 0
-266 ]
-267 def create-new-routine [
-268   local-scope
-269   n:&:num <- new number:type
-270   *n <- copy 34
-271   start-running new-routine, n
-272   # refcount of n decremented
-273 ]
-274 def new-routine n:&:num [
-275   local-scope
-276   load-ingredients
-277   1:num/raw <- copy *n
-278 ]
-279 # check that n wasn't reclaimed when create-new-routine returned
-280 +mem: storing 34 in location 1
-281 
-282 //: to support the previous scenario we'll increment refcounts for all call
-283 //: ingredients right at call time, and stop incrementing refcounts inside
-284 //: next-ingredient
-285 :(before "End Populate Call Ingredient")
-286 increment_any_refcounts(ingredient, ingredients.at(i));
-287 :(before "End Populate start-running Ingredient")
-288 increment_any_refcounts(ingredient, ingredients.at(i));
-289 :(after "should_update_refcounts() Special-cases When Writing Products Of Primitive Instructions")
-290 if (inst.operation == NEXT_INGREDIENT || inst.operation == NEXT_INGREDIENT_WITHOUT_TYPECHECKING) {
-291   if (space_index(inst.products.at(0)) > 0) return true;
-292   if (has_property(inst.products.at(0), "raw")) return true;
-293   return false;
-294 }
-295 
-296 // ensure this works with indirect calls using 'call' as well
-297 :(scenario start_running_immediately_updates_refcounts_of_ingredients_of_indirect_calls)
-298 % Scheduling_interval = 1;
-299 def main [
-300   local-scope
-301   n:&:num <- new number:type
-302   *n <- copy 34
-303   call f1, n
-304   1:num/raw <- copy *n
-305 ]
-306 def f1 n:&:num [
-307   local-scope
-308   load-ingredients
-309 ]
-310 # check that n wasn't reclaimed when f1 returned
-311 +mem: storing 34 in location 1
-312 
-313 :(scenario next_ingredient_never_leaks_refcounts)
-314 def create-space n:&:num -> default-space:space [
-315   default-space <- new location:type, 2
-316   load-ingredients
-317 ]
-318 def use-space [
-319   local-scope
-320   0:space/names:create-space <- next-ingredient
-321   n:&:num/space:1 <- next-ingredient  # should decrement refcount
-322   *n/space:1 <- copy 34
-323   n2:num <- add *n/space:1, 1
-324   return n2
-325 ]
-326 def main [
-327   local-scope
-328   n:&:num <- copy 12000/unsafe  # pretend allocation with a known address
-329   *n <- copy 23
-330   space:space <- create-space n
-331   n2:&:num <- copy 13000/unsafe
-332   n3:num <- use-space space, n2
-333 ]
-334 +run: {n: ("address" "number"), "space": "1"} <- next-ingredient
-335 +mem: decrementing refcount of 12000: 2 -> 1
-336 +run: {n: ("address" "number"), "space": "1", "lookup": ()} <- copy {34: "literal"}
-337 
-338 //: back to testing 'start-running'
-339 
-340 :(scenario start_running_returns_routine_id)
-341 def f1 [
-342   1:num <- start-running f2
-343 ]
-344 def f2 [
-345   12:num <- copy 44
-346 ]
-347 +mem: storing 2 in location 1
-348 
-349 //: this scenario will require some careful setup in escaped C++
-350 //: (straining our tangle capabilities to near-breaking point)
-351 :(scenario scheduler_skips_completed_routines)
-352 % recipe_ordinal f1 = load("recipe f1 [\n1:num <- copy 0\n]\n").front();
-353 % recipe_ordinal f2 = load("recipe f2 [\n2:num <- copy 0\n]\n").front();
-354 % Routines.push_back(new routine(f1));  // f1 meant to run
-355 % Routines.push_back(new routine(f2));
-356 % Routines.back()->state = COMPLETED;  // f2 not meant to run
-357 # must have at least one routine without escaping
-358 def f3 [
-359   3:num <- copy 0
-360 ]
-361 # by interleaving '+' lines with '-' lines, we allow f1 and f3 to run in any order
-362 +schedule: f1
-363 +mem: storing 0 in location 1
-364 -schedule: f2
-365 -mem: storing 0 in location 2
-366 +schedule: f3
-367 +mem: storing 0 in location 3
-368 
-369 :(scenario scheduler_starts_at_middle_of_routines)
-370 % Routines.push_back(new routine(COPY));
-371 % Routines.back()->state = COMPLETED;
-372 def f1 [
-373   1:num <- copy 0
-374   2:num <- copy 0
-375 ]
-376 +schedule: f1
-377 -run: idle
-378 
-379 //:: Errors in a routine cause it to terminate.
-380 
-381 :(scenario scheduler_terminates_routines_after_errors)
-382 % Hide_errors = true;
-383 % Scheduling_interval = 2;
-384 def f1 [
-385   start-running f2
-386   1:num <- copy 0
-387   2:num <- copy 0
-388 ]
-389 def f2 [
-390   # divide by 0 twice
-391   3:num <- divide-with-remainder 4, 0
-392   4:num <- divide-with-remainder 4, 0
-393 ]
-394 # f2 should stop after first divide by 0
-395 +error: f2: divide by zero in '3:num <- divide-with-remainder 4, 0'
-396 -error: f2: divide by zero in '4:num <- divide-with-remainder 4, 0'
-397 
-398 :(after "operator<<(ostream& os, unused end)")
-399   if (Trace_stream && Trace_stream->curr_label == "error" && Current_routine) {
-400   ¦ Current_routine->state = COMPLETED;
-401   }
-402 
-403 //:: Routines are marked completed when their parent completes.
-404 
-405 :(scenario scheduler_kills_orphans)
-406 def main [
-407   start-running f1
-408   # f1 never actually runs because its parent completes without waiting for it
-409 ]
-410 def f1 [
-411   1:num <- copy 0
-412 ]
-413 -schedule: f1
-414 
-415 :(before "End Scheduler Cleanup")
-416 for (int i = 0;  i < SIZE(Routines);  ++i) {
-417   if (Routines.at(i)->state == COMPLETED) continue;
-418   if (Routines.at(i)->parent_index < 0) continue;  // root thread
-419   // structured concurrency: http://250bpm.com/blog:71
-420   if (has_completed_parent(i)) {
-421   ¦ Routines.at(i)->state = COMPLETED;
-422   }
-423 }
-424 
-425 :(code)
-426 bool has_completed_parent(int routine_index) {
-427   for (int j = routine_index;  j >= 0;  j = Routines.at(j)->parent_index) {
-428   ¦ if (Routines.at(j)->state == COMPLETED)
-429   ¦ ¦ return true;
-430   }
-431   return false;
-432 }
-433 
-434 //:: 'routine-state' can tell if a given routine id is running
-435 
-436 :(scenario routine_state_test)
-437 % Scheduling_interval = 2;
-438 def f1 [
-439   1:num/child-id <- start-running f2
-440   12:num <- copy 0  # race condition since we don't care about location 12
-441   # thanks to Scheduling_interval, f2's one instruction runs in between here and completes
-442   2:num/state <- routine-state 1:num/child-id
-443 ]
-444 def f2 [
-445   12:num <- copy 0
-446   # trying to run a second instruction marks routine as completed
-447 ]
-448 # recipe f2 should be in state COMPLETED
-449 +mem: storing 1 in location 2
-450 
-451 :(before "End Primitive Recipe Declarations")
-452 ROUTINE_STATE,
-453 :(before "End Primitive Recipe Numbers")
-454 put(Recipe_ordinal, "routine-state", ROUTINE_STATE);
-455 :(before "End Primitive Recipe Checks")
-456 case ROUTINE_STATE: {
-457   if (SIZE(inst.ingredients) != 1) {
-458   ¦ raise << maybe(get(Recipe, r).name) << "'routine-state' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
-459   ¦ break;
-460   }
-461   if (!is_mu_number(inst.ingredients.at(0))) {
-462   ¦ raise << maybe(get(Recipe, r).name) << "first ingredient of 'routine-state' should be a routine id generated by 'start-running', but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
-463   ¦ break;
-464   }
-465   break;
-466 }
-467 :(before "End Primitive Recipe Implementations")
-468 case ROUTINE_STATE: {
-469   int id = ingredients.at(0).at(0);
-470   int result = -1;
-471   for (int i = 0;  i < SIZE(Routines);  ++i) {
-472   ¦ if (Routines.at(i)->id == id) {
-473   ¦ ¦ result = Routines.at(i)->state;
-474   ¦ ¦ break;
-475   ¦ }
-476   }
-477   products.resize(1);
-478   products.at(0).push_back(result);
-479   break;
-480 }
-481 
-482 //:: miscellaneous helpers
-483 
-484 :(before "End Primitive Recipe Declarations")
-485 STOP,
-486 :(before "End Primitive Recipe Numbers")
-487 put(Recipe_ordinal, "stop", STOP);
-488 :(before "End Primitive Recipe Checks")
-489 case STOP: {
-490   if (SIZE(inst.ingredients) != 1) {
-491   ¦ raise << maybe(get(Recipe, r).name) << "'stop' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
-492   ¦ break;
-493   }
-494   if (!is_mu_number(inst.ingredients.at(0))) {
-495   ¦ raise << maybe(get(Recipe, r).name) << "first ingredient of 'stop' should be a routine id generated by 'start-running', but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
-496   ¦ break;
-497   }
-498   break;
-499 }
-500 :(before "End Primitive Recipe Implementations")
-501 case STOP: {
-502   int id = ingredients.at(0).at(0);
-503   for (int i = 0;  i < SIZE(Routines);  ++i) {
-504   ¦ if (Routines.at(i)->id == id) {
-505   ¦ ¦ Routines.at(i)->state = COMPLETED;
-506   ¦ ¦ break;
-507   ¦ }
-508   }
-509   break;
-510 }
-511 
-512 :(before "End Primitive Recipe Declarations")
-513 _DUMP_ROUTINES,
-514 :(before "End Primitive Recipe Numbers")
-515 put(Recipe_ordinal, "$dump-routines", _DUMP_ROUTINES);
-516 :(before "End Primitive Recipe Checks")
-517 case _DUMP_ROUTINES: {
-518   break;
-519 }
-520 :(before "End Primitive Recipe Implementations")
-521 case _DUMP_ROUTINES: {
-522   for (int i = 0;  i < SIZE(Routines);  ++i) {
-523   ¦ cerr << i << ": " << Routines.at(i)->id << ' ' << Routines.at(i)->state << ' ' << Routines.at(i)->parent_index << '\n';
-524   }
-525   break;
-526 }
-527 
-528 //: support for stopping routines after some number of cycles
-529 
-530 :(scenario routine_discontinues_past_limit)
-531 % Scheduling_interval = 2;
-532 def f1 [
-533   1:num/child-id <- start-running f2
-534   limit-time 1:num/child-id, 10
-535   # padding loop just to make sure f2 has time to completed
-536   2:num <- copy 20
-537   2:num <- subtract 2:num, 1
-538   jump-if 2:num, -2:offset
-539 ]
-540 def f2 [
-541   jump -1:offset  # run forever
-542   $print [should never get here], 10/newline
-543 ]
-544 # f2 terminates
-545 +schedule: discontinuing routine 2
-546 
-547 :(before "End routine States")
-548 DISCONTINUED,
-549 :(before "End Scheduler State Transitions")
-550 if (Current_routine->limit >= 0) {
-551   if (Current_routine->limit <= Scheduling_interval) {
-552   ¦ trace(9999, "schedule") << "discontinuing routine " << Current_routine->id << end();
-553   ¦ Current_routine->state = DISCONTINUED;
-554   ¦ Current_routine->limit = 0;
-555   }
-556   else {
-557   ¦ Current_routine->limit -= Scheduling_interval;
-558   }
-559 }
-560 
-561 :(before "End Test Teardown")
-562 if (Passed && any_routines_with_error())
-563   raise << "some routines died with errors\n" << end();
-564 :(before "End Mu Test Teardown")
-565 if (Passed && any_routines_with_error())
-566   raise << Current_scenario->name << ": some routines died with errors\n" << end();
-567 
-568 :(code)
-569 bool any_routines_with_error() {
-570   for (int i = 0;  i < SIZE(Routines);  ++i) {
-571   ¦ if (Routines.at(i)->state == DISCONTINUED)
-572   ¦ ¦ return true;
-573   }
-574   return false;
-575 }
-576 
-577 :(before "End routine Fields")
-578 int limit;
-579 :(before "End routine Constructor")
-580 limit = -1;  /* no limit */
-581 
-582 :(before "End Primitive Recipe Declarations")
-583 LIMIT_TIME,
-584 :(before "End Primitive Recipe Numbers")
-585 put(Recipe_ordinal, "limit-time", LIMIT_TIME);
-586 :(before "End Primitive Recipe Checks")
-587 case LIMIT_TIME: {
-588   if (SIZE(inst.ingredients) != 2) {
-589   ¦ raise << maybe(get(Recipe, r).name) << "'limit-time' requires exactly two ingredient, but got '" << to_original_string(inst) << "'\n" << end();
-590   ¦ break;
-591   }
-592   if (!is_mu_number(inst.ingredients.at(0))) {
-593   ¦ raise << maybe(get(Recipe, r).name) << "first ingredient of 'limit-time' should be a routine id generated by 'start-running', but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
-594   ¦ break;
-595   }
-596   if (!is_mu_number(inst.ingredients.at(1))) {
-597   ¦ raise << maybe(get(Recipe, r).name) << "second ingredient of 'limit-time' should be a number (of instructions to run for), but got '" << inst.ingredients.at(1).original_string << "'\n" << end();
-598   ¦ break;
-599   }
-600   break;
-601 }
-602 :(before "End Primitive Recipe Implementations")
-603 case LIMIT_TIME: {
-604   int id = ingredients.at(0).at(0);
-605   for (int i = 0;  i < SIZE(Routines);  ++i) {
-606   ¦ if (Routines.at(i)->id == id) {
-607   ¦ ¦ Routines.at(i)->limit = ingredients.at(1).at(0);
-608   ¦ ¦ break;
-609   ¦ }
-610   }
-611   break;
-612 }
-613 
-614 :(before "End routine Fields")
-615 int instructions_run;
-616 :(before "End routine Constructor")
-617 instructions_run = 0;
-618 :(before "Reset instructions_run_this_scheduling_slice")
-619 Current_routine->instructions_run += Current_routine->instructions_run_this_scheduling_slice;
-620 :(before "End Primitive Recipe Declarations")
-621 NUMBER_OF_INSTRUCTIONS,
-622 :(before "End Primitive Recipe Numbers")
-623 put(Recipe_ordinal, "number-of-instructions", NUMBER_OF_INSTRUCTIONS);
-624 :(before "End Primitive Recipe Checks")
-625 case NUMBER_OF_INSTRUCTIONS: {
-626   if (SIZE(inst.ingredients) != 1) {
-627   ¦ raise << maybe(get(Recipe, r).name) << "'number-of-instructions' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
-628   ¦ break;
-629   }
-630   if (!is_mu_number(inst.ingredients.at(0))) {
-631   ¦ raise << maybe(get(Recipe, r).name) << "first ingredient of 'number-of-instructions' should be a routine id generated by 'start-running', but got '" << inst.ingredients.at(0).original_string << "'\n" << end();
-632   ¦ break;
-633   }
-634   break;
-635 }
-636 :(before "End Primitive Recipe Implementations")
-637 case NUMBER_OF_INSTRUCTIONS: {
-638   int id = ingredients.at(0).at(0);
-639   int result = -1;
-640   for (int i = 0;  i < SIZE(Routines);  ++i) {
-641   ¦ if (Routines.at(i)->id == id) {
-642   ¦ ¦ result = Routines.at(i)->instructions_run;
-643   ¦ ¦ break;
-644   ¦ }
-645   }
-646   products.resize(1);
-647   products.at(0).push_back(result);
-648   break;
-649 }
-650 
-651 :(scenario number_of_instructions)
-652 def f1 [
-653   10:num/child-id <- start-running f2
-654   {
-655   ¦ loop-unless 20:num
-656   }
-657   11:num <- number-of-instructions 10:num
-658 ]
-659 def f2 [
-660   # 2 instructions worth of work
-661   1:num <- copy 34
-662   20:num <- copy 1
-663 ]
-664 # f2 runs an extra instruction for the implicit return added by the
-665 # fill_in_return_ingredients transform
-666 +mem: storing 3 in location 11
-667 
-668 :(scenario number_of_instructions_across_multiple_scheduling_intervals)
-669 % Scheduling_interval = 1;
-670 def f1 [
-671   10:num/child-id <- start-running f2
-672   {
-673   ¦ loop-unless 20:num
-674   }
-675   11:num <- number-of-instructions 10:num
-676 ]
-677 def f2 [
-678   # 4 instructions worth of work
-679   1:num <- copy 34
-680   2:num <- copy 1
-681   2:num <- copy 3
-682   20:num <- copy 1
-683 ]
-684 # f2 runs an extra instruction for the implicit return added by the
-685 # fill_in_return_ingredients transform
-686 +mem: storing 5 in location 11
-687 
-688 //:: make sure that each routine gets a different alloc to start
-689 
-690 :(scenario new_concurrent)
-691 def f1 [
-692   start-running f2
-693   1:&:num/raw <- new number:type
-694   # wait for f2 to complete
-695   {
-696   ¦ loop-unless 4:num/raw
-697   }
-698 ]
-699 def f2 [
-700   2:&:num/raw <- new number:type
-701   # hack: assumes scheduler implementation
-702   3:bool/raw <- equal 1:&:num/raw, 2:&:num/raw
-703   # signal f2 complete
-704   4:num/raw <- copy 1
-705 ]
-706 +mem: storing 0 in location 3
-
- - - -- cgit 1.4.1-2-gfad0