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 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 ¦
75 ¦ if (Current_routine->completed())
76 ¦ ¦ Current_routine->state = COMPLETED;
77 ¦
78
79 ¦
80 ¦
81 }
82
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
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
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
127
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);
134 ¦ current_call().ingredient_atoms.push_back(arg);
135 }
136 run(main_routine);
137 }
138
139
140
141
142
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
154 :(before "End routine Fields")
155
156 int 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
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 ingredient = current_instruction().ingredients.at(i);
184 ¦ canonize_type(ingredient);
185 ¦ new_routine->calls.front().ingredients.push_back(ingredient);
186 ¦
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
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
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
253 :(before "End is_indirect_call_with_ingredients Special-cases")
254 if (r == START_RUNNING) return true;
255
256
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
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
273 ]
274 def new-routine n:&:num [
275 local-scope
276 load-ingredients
277 1:num/raw <- copy *n
278 ]
279
280 +mem: storing 34 in location 1
281
282
283
284
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
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
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
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
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
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
350
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
358 def f3 [
359 3:num <- copy 0
360 ]
361
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
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
391 3:num <- divide-with-remainder 4, 0
392 4:num <- divide-with-remainder 4, 0
393 ]
394
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
404
405 :(scenario scheduler_kills_orphans)
406 def main [
407 start-running f1
408
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;
419
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
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
441
442 2:num/state <- routine-state 1:num/child-id
443 ]
444 def f2 [
445 12:num <- copy 0
446
447 ]
448
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
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
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
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
542 $print [should never get here], 10/newline
543 ]
544
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;
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
661 1:num <- copy 34
662 20:num <- copy 1
663 ]
664
665
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
679 1:num <- copy 34
680 2:num <- copy 1
681 2:num <- copy 3
682 20:num <- copy 1
683 ]
684
685
686 +mem: storing 5 in location 11
687
688
689
690 :(scenario new_concurrent)
691 def f1 [
692 start-running f2
693 1:&:num/raw <- new number:type
694
695 {
696 ¦ loop-unless 4:num/raw
697 }
698 ]
699 def f2 [
700 2:&:num/raw <- new number:type
701
702 3:bool/raw <- equal 1:&:num/raw, 2:&:num/raw
703
704 4:num/raw <- copy 1
705 ]
706 +mem: storing 0 in location 3