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