1
2
3
4 :(scenario scheduler)
5 def f1 [
6 start-running f2
7
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
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);
30 return Current_routine->state == RUNNING
31 ¦ ¦ && Current_routine->instructions_run_this_scheduling_slice < Scheduling_interval;
32 }
33 :(after "stop_running_current_routine:")
34
35 Current_routine->instructions_run_this_scheduling_slice = 0;
36
37
38
39 :(before "struct routine")
40 enum routine_state {
41 RUNNING,
42 COMPLETED,
43
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 Setup")
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 ¦
72 ¦ if (Current_routine->completed())
73 ¦ ¦ Current_routine->state = COMPLETED;
74 ¦
75
76 ¦
77 ¦
78 }
79
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
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
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
130
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);
137 ¦ current_call().ingredient_atoms.push_back(arg);
138 }
139 run(main_routine);
140 }
141
142
143
144
145
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
157 :(before "End routine Fields")
158
159 int 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
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 ingredient = current_instruction().ingredients.at(i);
187 ¦ canonize_type(ingredient);
188 ¦ new_routine->calls.front().ingredients.push_back(ingredient);
189 ¦
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
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
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
256 :(before "End is_indirect_call_with_ingredients Special-cases")
257 if (r == START_RUNNING) return true;
258
259
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
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
276 ]
277 def new-routine n:&:num [
278 local-scope
279 load-ingredients
280 1:num/raw <- copy *n
281 ]
282
283 +mem: storing 34 in location 1
284
285
286
287
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
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
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
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
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
342
343 :(scenario start_running_returns_routine_id)
344 def f1 [
345 1:num <- start-running f2
346 ]
347 def f2 [
348 12:num <- copy 44
349 ]
350 +mem: storing 2 in location 1
351
352
353
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
361 def f3 [
362 3:num <- copy 0
363 ]
364
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
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
394 3:num <- divide-with-remainder 4, 0
395 4:num <- divide-with-remainder 4, 0
396 ]
397
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
407
408 :(scenario scheduler_kills_orphans)
409 def main [
410 start-running f1
411
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;
422
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
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
444
445 2:num/state <- routine-state 1:num/child-id
446 ]
447 def f2 [
448 12:num <- copy 0
449
450 ]
451
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 '" << inst.original_string << "'\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
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 '" << inst.original_string << "'\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
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
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
545 $print [should never get here], 10/newline
546 ]
547
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;
558 }
559 else {
560 ¦ Current_routine->limit -= Scheduling_interval;
561 }
562 }
563
564 :(before "End 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;
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 '" << inst.original_string << "'\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 '" << inst.original_string << "'\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
664 1:num <- copy 34
665 20:num <- copy 1
666 ]
667
668
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
682 1:num <- copy 34
683 2:num <- copy 1
684 2:num <- copy 3
685 20:num <- copy 1
686 ]
687
688
689 +mem: storing 5 in location 11
690
691
692
693 :(scenario new_concurrent)
694 def f1 [
695 start-running f2
696 1:&:num/raw <- new number:type
697
698 {
699 ¦ loop-unless 4:num/raw
700 }
701 ]
702 def f2 [
703 2:&:num/raw <- new number:type
704
705 3:bool/raw <- equal 1:&:num/raw, 2:&:num/raw
706
707 4:num/raw <- copy 1
708 ]
709 +mem: storing 0 in location 3