//: Run a second routine concurrently using 'start-running', without any //: guarantees on how the operations in each are interleaved with each other. :(scenario scheduler) def f1 [ start-running f2 # wait for f2 to run { jump-unless 1:num, -1 } ] def f2 [ 1:num <- copy 1 ] +schedule: f1 +schedule: f2 //: first, add a deadline to run(routine) :(before "End Globals") int Scheduling_interval = 500; :(before "End routine Fields") int instructions_run_this_scheduling_slice; :(before "End routine Constructor") instructions_run_this_scheduling_slice = 0; :(after "Running One Instruction") ++Current_routine->instructions_run_this_scheduling_slice; :(replace{} "bool should_continue_running(const routine* current_routine)") bool should_continue_running(const routine* current_routine) { assert(current_routine == Current_routine); // argument passed in just to make caller readable above return Current_routine->state == RUNNING && Current_routine->instructions_run_this_scheduling_slice < Scheduling_interval; } :(after "stop_running_current_routine:") // Reset instructions_run_this_scheduling_slice Current_routine->instructions_run_this_scheduling_slice = 0; //: now the rest of the scheduler is clean :(before "struct routine") enum routine_state { RUNNING, COMPLETED, // End routine States }; :(before "End routine Fields") enum routine_state state; :(before "End routine Constructor") state = RUNNING; :(before "End Globals") vector Routines; int Current_routine_index = 0; :(before "End Reset") Scheduling_interval = 500; for (int i = 0; i < SIZE(Routines); ++i) delete Routines.at(i); Routines.clear(); Current_routine = NULL; :(replace{} "void run(const recipe_ordinal r)") void run(const recipe_ordinal r) { run(new routine(r)); } :(code) void run(routine* rr) { Routines.push_back(rr); Current_routine_index = 0, Current_routine = Routines.at(0); while (!all_routines_done()) { skip_to_next_routine(); assert(Current_routine); assert(Current_routine->state == RUNNING); trace(9990, "schedule") << current_routine_label() << end(); run_current_routine(); // Scheduler State Transitions if (Current_routine->completed()) Current_routine->state = COMPLETED; // End Scheduler State Transitions // Scheduler Cleanup // End Scheduler Cleanup } // End Run Routine } bool all_routines_done() { for (int i = 0; i < SIZE(Routines); ++i) { if (Routines.at(i)->state == RUNNING) return false; } return true; } // skip Current_routine_index past non-RUNNING routines void skip_to_next_routine() { assert(!Routines.empty()); assert(Current_routine_index < SIZE(Routines)); for (int i = (Current_routine_index+1)%SIZE(Routines); i != Current_routine_index; i = (i+1)%SIZE(Routines)) { if (Routines.at(i)->state == RUNNING) { Current_routine_index = i; Current_routine = Routines.at(i); return; } } } string current_routine_label() { return routine_label(Current_routine); } string routine_label(routine* r) { ostringstream result; const call_stack& calls = r->calls; for (call_stack::const_iterator p = calls.begin(); p != calls.end(); ++p) { if (p != calls.begin()) result << '/'; result << get(Recipe, p->running_recipe).name; } return result.str(); } //: special case for the very first routine :(replace{} "void run_main(int argc, char* argv[])") void run_main(int argc, char* argv[]) { recipe_ordinal r = get(Recipe_ordinal, "main"); assert(r); routine* main_routine = new routine(r); // pass in commandline args as ingredients to main // todo: test this Current_routine = main_routine; for (int i = 1; i < argc; ++i) { vector arg; arg.push_back(new_mu_text(argv[i])); assert(get(Memory, arg.back()) == 0); current_call().ingredient_atoms.push_back(arg); } run(main_routine); } //:: To schedule new routines to run, call 'start-running'. //: 'start-running' will return a unique id for the routine that was created. //: routine id is a number, but don't do any arithmetic on it :(before "End routine Fields") int id; :(before "End Globals") int Next_routine_id = 1; :(before "End Reset") Next_routine_id = 1; :(before "End routine Constructor") id = Next_routine_id; ++Next_routine_id; //: routines save the routine that spawned them :(before "End routine Fields") // todo: really should be routine_id, but that's less efficient. int parent_index; // only < 0 if there's no parent_index :(before "End routine Constructor") parent_index = -1; :(before "End Primitive Recipe Declarations") START_RUNNING, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "start-running", START_RUNNING); :(before "End Primitive Recipe Checks") case START_RUNNING: { if (inst.ingredients.empty()) { raise << maybe(get(Recipe, r).name) << "'start-running' requires at least one ingredient: the recipe to start running\n" << end(); break; } if (!is_mu_recipe(inst.ingredients.at(0))) { 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(); break; } break; } :(before "End Primitive Recipe Implem
# tests for 'scenario' in previous layer

scenario first_scenario_in_mu [
  run [
    10:num <- add 2, 2
  ]
  memory-should-contain [
    10 <- 4
  ]
]

scenario scenario_with_comment_in_mu [
  run [
    # comment
    10:num <- add 2, 2
  ]
  memory-should-contain [
    10 <- 4
  ]
]

scenario scenario_with_multiple_comments_in_mu [
  run [
    # comment1
    # comment2
    10:num <- add 2, 2
  ]
  memory-should-contain [
    10 <- 4
  ]
]

scenario check_text_in_memory [
  run [
    10:num <- copy 3
    11:char <- copy 97  # 'a'
    12:char <- copy 98  # 'b'
    13:char <- copy 99  # 'c'
  ]
  memory-should-contain [
    10:array:character <- [abc]
  ]
]

scenario check_trace [
  run [
    10:num <- add 2, 2
  ]
  trace-should-contain [
    mem: storing 4 in location 10
  ]
]