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 ¦ reagent ingredient = current_instruction().ingredients.at(i);
183 ¦ new_routine->calls.front().ingredients.push_back(ingredient);
184 ¦ vector<double> new_ingredient_atoms = deep_copy(ingredient);
185 ¦ new_routine->calls.front().ingredient_atoms.push_back(new_ingredient_atoms);
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 :(scenario start_running_immediately_updates_refcounts_of_ingredients_of_indirect_calls)
284 % Scheduling_interval = 1;
285 def main [
286 local-scope
287 n:&:num <- new number:type
288 *n <- copy 34
289 call f1, n
290 1:num/raw <- copy *n
291 ]
292 def f1 n:&:num [
293 local-scope
294 load-ingredients
295 ]
296
297 +mem: storing 34 in location 1
298
299 :(scenario next_ingredient_never_leaks_refcounts)
300 def create-space n:&:num -> default-space:space [
301 default-space <- new location:type, 2
302 load-ingredients
303 ]
304 def use-space [
305 local-scope
306 0:space/names:create-space <- next-ingredient
307 n:&:num/space:1 <- next-ingredient
308 *n/space:1 <- copy 34
309 n2:num <- add *n/space:1, 1
310 return n2
311 ]
312 def main [
313 local-scope
314 n:&:num <- copy 12000/unsafe
315 *n <- copy 23
316 space:space/names:create-space <- create-space n
317 n2:&:num <- copy 13000/unsafe
318 n3:num <- use-space space, n2
319 ]
320 +run: {n: ("address" "number"), "space": "1"} <- next-ingredient
321 +mem: decrementing refcount of 12000: 2 -> 1
322 +run: {n: ("address" "number"), "space": "1", "lookup": ()} <- copy {34: "literal"}
323
324
325
326 :(scenario start_running_returns_routine_id)
327 def f1 [
328 1:num <- start-running f2
329 ]
330 def f2 [
331 12:num <- copy 44
332 ]
333 +mem: storing 2 in location 1
334
335
336
337 :(scenario scheduler_skips_completed_routines)
338 % recipe_ordinal f1 = load("recipe f1 [\n1:num <- copy 0\n]\n").front();
339 % recipe_ordinal f2 = load("recipe f2 [\n2:num <- copy 0\n]\n").front();
340 % Routines.push_back(new routine(f1)); // f1 meant to run
341 % Routines.push_back(new routine(f2));
342 % Routines.back()->state = COMPLETED; // f2 not meant to run
343
344 def f3 [
345 3:num <- copy 0
346 ]
347
348 +schedule: f1
349 +mem: storing 0 in location 1
350 -schedule: f2
351 -mem: storing 0 in location 2
352 +schedule: f3
353 +mem: storing 0 in location 3
354
355 :(scenario scheduler_starts_at_middle_of_routines)
356 % Routines.push_back(new routine(COPY));
357 % Routines.back()->state = COMPLETED;
358 def f1 [
359 1:num <- copy 0
360 2:num <- copy 0
361 ]
362 +schedule: f1
363 -run: idle
364
365
366
367 :(scenario scheduler_terminates_routines_after_errors)
368 % Hide_errors = true;
369 % Scheduling_interval = 2;
370 def f1 [
371 start-running f2
372 1:num <- copy 0
373 2:num <- copy 0
374 ]
375 def f2 [
376
377 3:num <- divide-with-remainder 4, 0
378 4:num <- divide-with-remainder 4, 0
379 ]
380
381 +error: f2: divide by zero in '3:num <- divide-with-remainder 4, 0'
382 -error: f2: divide by zero in '4:num <- divide-with-remainder 4, 0'
383
384 :(after "operator<<(ostream& os, unused end)")
385 if (Trace_stream && Trace_stream->curr_label == "error" && Current_routine) {
386 ¦ Current_routine->state = COMPLETED;
387 }
388
389
390
391 :(scenario scheduler_kills_orphans)
392 def main [
393 start-running f1
394
395 ]
396 def f1 [
397 1:num <- copy 0
398 ]
399 -schedule: f1
400
401 :(before "End Scheduler Cleanup")
402 for (int i = 0; i < SIZE(Routines); ++i) {
403 if (Routines.at(i)->state == COMPLETED) continue;
404 if (Routines.at(i)->parent_index < 0) continue;
405
406 if (has_completed_parent(i)) {
407 ¦ Routines.at(i)->state = COMPLETED;
408 }
409 }
410
411 :(code)
412 bool has_completed_parent(int routine_index) {
413 for (int j = routine_index; j >= 0; j = Routines.at(j)->parent_index) {
414 ¦ if (Routines.at(j)->state == COMPLETED)
415 ¦ ¦ return true;
416 }
417 return false;
418 }
419
420
421
422 :(scenario routine_state_test)
423 % Scheduling_interval = 2;
424 def f1 [
425 1:num/child-id <- start-running f2
426 12:num <- copy 0
427
428 2:num/state <- routine-state 1:num/child-id
429 ]
430 def f2 [
431 12:num <- copy 0
432
433 ]
434
435 +mem: storing 1 in location 2
436
437 :(before "End Primitive Recipe Declarations")
438 ROUTINE_STATE,
439 :(before "End Primitive Recipe Numbers")
440 put(Recipe_ordinal, "routine-state", ROUTINE_STATE);
441 :(before "End Primitive Recipe Checks")
442 case ROUTINE_STATE: {
443 if (SIZE(inst.ingredients) != 1) {
444 ¦ raise << maybe(get(Recipe, r).name) << "'routine-state' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
445 ¦ break;
446 }
447 if (!is_mu_number(inst.ingredients.at(0))) {
448 ¦ 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();
449 ¦ break;
450 }
451 break;
452 }
453 :(before "End Primitive Recipe Implementations")
454 case ROUTINE_STATE: {
455 int id = ingredients.at(0).at(0);
456 int result = -1;
457 for (int i = 0; i < SIZE(Routines); ++i) {
458 ¦ if (Routines.at(i)->id == id) {
459 ¦ ¦ result = Routines.at(i)->state;
460 ¦ ¦ break;
461 ¦ }
462 }
463 products.resize(1);
464 products.at(0).push_back(result);
465 break;
466 }
467
468
469
470 :(before "End Primitive Recipe Declarations")
471 STOP,
472 :(before "End Primitive Recipe Numbers")
473 put(Recipe_ordinal, "stop", STOP);
474 :(before "End Primitive Recipe Checks")
475 case STOP: {
476 if (SIZE(inst.ingredients) != 1) {
477 ¦ raise << maybe(get(Recipe, r).name) << "'stop' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
478 ¦ break;
479 }
480 if (!is_mu_number(inst.ingredients.at(0))) {
481 ¦ 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();
482 ¦ break;
483 }
484 break;
485 }
486 :(before "End Primitive Recipe Implementations")
487 case STOP: {
488 int id = ingredients.at(0).at(0);
489 for (int i = 0; i < SIZE(Routines); ++i) {
490 ¦ if (Routines.at(i)->id == id) {
491 ¦ ¦ Routines.at(i)->state = COMPLETED;
492 ¦ ¦ break;
493 ¦ }
494 }
495 break;
496 }
497
498 :(before "End Primitive Recipe Declarations")
499 _DUMP_ROUTINES,
500 :(before "End Primitive Recipe Numbers")
501 put(Recipe_ordinal, "$dump-routines", _DUMP_ROUTINES);
502 :(before "End Primitive Recipe Checks")
503 case _DUMP_ROUTINES: {
504 break;
505 }
506 :(before "End Primitive Recipe Implementations")
507 case _DUMP_ROUTINES: {
508 for (int i = 0; i < SIZE(Routines); ++i) {
509 ¦ cerr << i << ": " << Routines.at(i)->id << ' ' << Routines.at(i)->state << ' ' << Routines.at(i)->parent_index << '\n';
510 }
511 break;
512 }
513
514
515
516 :(scenario routine_discontinues_past_limit)
517 % Scheduling_interval = 2;
518 def f1 [
519 1:num/child-id <- start-running f2
520 limit-time 1:num/child-id, 10
521
522 2:num <- copy 20
523 2:num <- subtract 2:num, 1
524 jump-if 2:num, -2:offset
525 ]
526 def f2 [
527 jump -1:offset
528 $print [should never get here], 10/newline
529 ]
530
531 +schedule: discontinuing routine 2
532
533 :(before "End routine States")
534 DISCONTINUED,
535 :(before "End Scheduler State Transitions")
536 if (Current_routine->limit >= 0) {
537 if (Current_routine->limit <= Scheduling_interval) {
538 ¦ trace("schedule") << "discontinuing routine " << Current_routine->id << end();
539 ¦ Current_routine->state = DISCONTINUED;
540 ¦ Current_routine->limit = 0;
541 }
542 else {
543 ¦ Current_routine->limit -= Scheduling_interval;
544 }
545 }
546
547 :(before "End Test Teardown")
548 if (Passed && any_routines_with_error())
549 raise << "some routines died with errors\n" << end();
550 :(before "End Mu Test Teardown")
551 if (Passed && any_routines_with_error())
552 raise << Current_scenario->name << ": some routines died with errors\n" << end();
553
554 :(code)
555 bool any_routines_with_error() {
556 for (int i = 0; i < SIZE(Routines); ++i) {
557 ¦ if (Routines.at(i)->state == DISCONTINUED)
558 ¦ ¦ return true;
559 }
560 return false;
561 }
562
563 :(before "End routine Fields")
564 int limit;
565 :(before "End routine Constructor")
566 limit = -1;
567
568 :(before "End Primitive Recipe Declarations")
569 LIMIT_TIME,
570 :(before "End Primitive Recipe Numbers")
571 put(Recipe_ordinal, "limit-time", LIMIT_TIME);
572 :(before "End Primitive Recipe Checks")
573 case LIMIT_TIME: {
574 if (SIZE(inst.ingredients) != 2) {
575 ¦ raise << maybe(get(Recipe, r).name) << "'limit-time' requires exactly two ingredient, but got '" << to_original_string(inst) << "'\n" << end();
576 ¦ break;
577 }
578 if (!is_mu_number(inst.ingredients.at(0))) {
579 ¦ 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();
580 ¦ break;
581 }
582 if (!is_mu_number(inst.ingredients.at(1))) {
583 ¦ 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();
584 ¦ break;
585 }
586 break;
587 }
588 :(before "End Primitive Recipe Implementations")
589 case LIMIT_TIME: {
590 int id = ingredients.at(0).at(0);
591 for (int i = 0; i < SIZE(Routines); ++i) {
592 ¦ if (Routines.at(i)->id == id) {
593 ¦ ¦ Routines.at(i)->limit = ingredients.at(1).at(0);
594 ¦ ¦ break;
595 ¦ }
596 }
597 break;
598 }
599
600 :(before "End routine Fields")
601 int instructions_run;
602 :(before "End routine Constructor")
603 instructions_run = 0;
604 :(before "Reset instructions_run_this_scheduling_slice")
605 Current_routine->instructions_run += Current_routine->instructions_run_this_scheduling_slice;
606 :(before "End Primitive Recipe Declarations")
607 NUMBER_OF_INSTRUCTIONS,
608 :(before "End Primitive Recipe Numbers")
609 put(Recipe_ordinal, "number-of-instructions", NUMBER_OF_INSTRUCTIONS);
610 :(before "End Primitive Recipe Checks")
611 case NUMBER_OF_INSTRUCTIONS: {
612 if (SIZE(inst.ingredients) != 1) {
613 ¦ raise << maybe(get(Recipe, r).name) << "'number-of-instructions' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end();
614 ¦ break;
615 }
616 if (!is_mu_number(inst.ingredients.at(0))) {
617 ¦ 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();
618 ¦ break;
619 }
620 break;
621 }
622 :(before "End Primitive Recipe Implementations")
623 case NUMBER_OF_INSTRUCTIONS: {
624 int id = ingredients.at(0).at(0);
625 int result = -1;
626 for (int i = 0; i < SIZE(Routines); ++i) {
627 ¦ if (Routines.at(i)->id == id) {
628 ¦ ¦ result = Routines.at(i)->instructions_run;
629 ¦ ¦ break;
630 ¦ }
631 }
632 products.resize(1);
633 products.at(0).push_back(result);
634 break;
635 }
636
637 :(scenario number_of_instructions)
638 def f1 [
639 10:num/child-id <- start-running f2
640 {
641 ¦ loop-unless 20:num
642 }
643 11:num <- number-of-instructions 10:num
644 ]
645 def f2 [
646
647 1:num <- copy 34
648 20:num <- copy 1
649 ]
650
651
652 +mem: storing 3 in location 11
653
654 :(scenario number_of_instructions_across_multiple_scheduling_intervals)
655 % Scheduling_interval = 1;
656 def f1 [
657 10:num/child-id <- start-running f2
658 {
659 ¦ loop-unless 20:num
660 }
661 11:num <- number-of-instructions 10:num
662 ]
663 def f2 [
664
665 1:num <- copy 34
666 2:num <- copy 1
667 2:num <- copy 3
668 20:num <- copy 1
669 ]
670
671
672 +mem: storing 5 in location 11
673
674
675
676 :(scenario new_concurrent)
677 def f1 [
678 start-running f2
679 1:&:num/raw <- new number:type
680
681 {
682 ¦ loop-unless 4:num/raw
683 }
684 ]
685 def f2 [
686 2:&:num/raw <- new number:type
687
688 3:bool/raw <- equal 1:&:num/raw, 2:&:num/raw
689
690 4:num/raw <- copy 1
691 ]
692 +mem: storing 0 in location 3