2015-05-06 16:15:08 +00:00
//: Run a second routine concurrently using 'start-running', without any
//: guarantees on how the operations in each are interleaved with each other.
2015-04-24 03:42:17 +00:00
2015-04-25 07:00:49 +00:00
: ( scenario scheduler )
2016-03-08 09:30:14 +00:00
def f1 [
2016-01-18 07:15:03 +00:00
start - running f2
2015-05-10 12:42:56 +00:00
# wait for f2 to run
{
2015-07-28 21:33:22 +00:00
jump - unless 1 : number , - 1
2015-05-10 12:42:56 +00:00
}
2015-04-25 04:39:09 +00:00
]
2016-03-08 09:30:14 +00:00
def f2 [
2015-07-28 21:33:22 +00:00
1 : number < - copy 1
2015-04-25 04:39:09 +00:00
]
+ schedule : f1
+ schedule : f2
2015-04-24 03:42:17 +00:00
2015-04-25 04:39:09 +00:00
//: first, add a deadline to run(routine)
//: these changes are ugly and brittle; just close your nose and get through the next few lines
2015-04-25 02:58:38 +00:00
: ( replace " void run_current_routine() " )
2016-03-14 03:26:47 +00:00
void run_current_routine ( int time_slice )
: ( replace " while (!Current_routine->completed()) " following " void run_current_routine(int time_slice) " )
int ninstrs = 0 ;
2015-04-27 08:42:07 +00:00
while ( Current_routine - > state = = RUNNING & & ninstrs < time_slice )
2015-04-24 03:42:17 +00:00
: ( after " Running One Instruction " )
ninstrs + + ;
2015-04-25 04:39:09 +00:00
//: now the rest of the scheduler is clean
2015-04-27 05:44:47 +00:00
: ( before " struct routine " )
enum routine_state {
RUNNING ,
COMPLETED ,
2015-04-27 08:42:07 +00:00
// End routine States
2015-04-27 05:44:47 +00:00
} ;
: ( before " End routine Fields " )
enum routine_state state ;
: ( before " End routine Constructor " )
state = RUNNING ;
2015-04-25 04:39:09 +00:00
: ( before " End Globals " )
2015-04-27 05:44:47 +00:00
vector < routine * > Routines ;
2016-03-14 03:26:47 +00:00
int Current_routine_index = 0 ;
int Scheduling_interval = 500 ;
2015-04-25 07:00:49 +00:00
: ( before " End Setup " )
Scheduling_interval = 500 ;
2015-06-23 04:16:31 +00:00
Routines . clear ( ) ;
2015-07-04 16:40:50 +00:00
: ( replace { } " void run(recipe_ordinal r) " )
void run ( recipe_ordinal r ) {
2015-09-06 19:25:51 +00:00
run ( new routine ( r ) ) ;
}
: ( code )
void run ( routine * rr ) {
Routines . push_back ( rr ) ;
2015-05-07 22:49:40 +00:00
Current_routine_index = 0 , Current_routine = Routines . at ( 0 ) ;
2015-04-27 05:44:47 +00:00
while ( ! all_routines_done ( ) ) {
2015-05-02 18:49:11 +00:00
skip_to_next_routine ( ) ;
2015-04-27 05:44:47 +00:00
assert ( Current_routine ) ;
assert ( Current_routine - > state = = RUNNING ) ;
2015-10-29 18:56:10 +00:00
trace ( 9990 , " schedule " ) < < current_routine_label ( ) < < end ( ) ;
2016-08-26 20:27:57 +00:00
//? cerr << "schedule: " << current_routine_label() << '\n';
2015-04-25 04:39:09 +00:00
run_current_routine ( Scheduling_interval ) ;
2015-05-09 18:35:47 +00:00
// Scheduler State Transitions
2015-04-26 05:30:20 +00:00
if ( Current_routine - > completed ( ) )
2015-04-27 05:44:47 +00:00
Current_routine - > state = COMPLETED ;
// End Scheduler State Transitions
2015-05-09 18:35:47 +00:00
// Scheduler Cleanup
// End Scheduler Cleanup
2015-04-27 05:44:47 +00:00
}
}
bool all_routines_done ( ) {
2016-03-14 03:26:47 +00:00
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
2016-04-21 02:44:11 +00:00
if ( Routines . at ( i ) - > state = = RUNNING )
2015-04-27 05:44:47 +00:00
return false ;
}
return true ;
}
// skip Current_routine_index past non-RUNNING routines
void skip_to_next_routine ( ) {
assert ( ! Routines . empty ( ) ) ;
2015-05-17 09:22:41 +00:00
assert ( Current_routine_index < SIZE ( Routines ) ) ;
2016-03-14 03:26:47 +00:00
for ( int i = ( Current_routine_index + 1 ) % SIZE ( Routines ) ; i ! = Current_routine_index ; i = ( i + 1 ) % SIZE ( Routines ) ) {
2015-05-07 22:49:40 +00:00
if ( Routines . at ( i ) - > state = = RUNNING ) {
2015-04-27 05:44:47 +00:00
Current_routine_index = i ;
2015-05-07 22:49:40 +00:00
Current_routine = Routines . at ( i ) ;
2015-04-27 05:44:47 +00:00
return ;
}
2015-04-25 04:39:09 +00:00
}
}
2015-05-13 23:33:40 +00:00
string current_routine_label ( ) {
ostringstream result ;
2015-10-28 20:08:26 +00:00
const call_stack & calls = Current_routine - > calls ;
for ( call_stack : : const_iterator p = calls . begin ( ) ; p ! = calls . end ( ) ; + + p ) {
2015-05-13 23:33:40 +00:00
if ( p ! = calls . begin ( ) ) result < < ' / ' ;
2015-11-06 19:06:58 +00:00
result < < get ( Recipe , p - > running_recipe ) . name ;
2015-05-13 23:33:40 +00:00
}
return result . str ( ) ;
}
2015-04-25 04:39:09 +00:00
: ( before " End Teardown " )
2016-03-14 03:26:47 +00:00
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i )
2015-05-07 22:49:40 +00:00
delete Routines . at ( i ) ;
2015-04-27 05:44:47 +00:00
Routines . clear ( ) ;
2015-09-12 22:33:21 +00:00
Current_routine = NULL ;
2015-04-25 04:39:09 +00:00
2015-09-06 19:25:51 +00:00
//: special case for the very first routine
: ( replace { } " void run_main(int argc, char* argv[]) " )
void run_main ( int argc , char * argv [ ] ) {
2015-11-28 01:49:30 +00:00
recipe_ordinal r = get ( Recipe_ordinal , " main " ) ;
2016-01-26 03:32:03 +00:00
assert ( r ) ;
routine * main_routine = new routine ( r ) ;
// pass in commandline args as ingredients to main
// todo: test this
Current_routine = main_routine ;
2016-03-14 03:26:47 +00:00
for ( int i = 1 ; i < argc ; + + i ) {
2016-01-26 03:32:03 +00:00
vector < double > arg ;
arg . push_back ( new_mu_string ( argv [ i ] ) ) ;
current_call ( ) . ingredient_atoms . push_back ( arg ) ;
2015-09-06 19:25:51 +00:00
}
2016-01-26 03:32:03 +00:00
run ( main_routine ) ;
2015-09-06 19:25:51 +00:00
}
2015-05-06 16:15:08 +00:00
//:: To schedule new routines to run, call 'start-running'.
2015-05-06 00:41:10 +00:00
2015-05-06 16:15:08 +00:00
//: 'start-running' will return a unique id for the routine that was created.
2015-05-13 17:03:26 +00:00
//: routine id is a number, but don't do any arithmetic on it
2015-05-06 00:41:10 +00:00
: ( before " End routine Fields " )
2016-03-14 03:26:47 +00:00
int id ;
2015-05-06 00:41:10 +00:00
: ( before " End Globals " )
2016-03-14 03:26:47 +00:00
int Next_routine_id = 1 ;
2015-05-06 00:41:10 +00:00
: ( before " End Setup " )
Next_routine_id = 1 ;
: ( before " End routine Constructor " )
id = Next_routine_id ;
Next_routine_id + + ;
2015-05-09 18:35:47 +00:00
//: routines save the routine that spawned them
: ( before " End routine Fields " )
// todo: really should be routine_id, but that's less efficient.
2016-03-14 03:26:47 +00:00
int parent_index ; // only < 0 if there's no parent_index
2015-05-09 18:35:47 +00:00
: ( before " End routine Constructor " )
parent_index = - 1 ;
2015-04-25 04:39:09 +00:00
: ( before " End Primitive Recipe Declarations " )
2015-05-01 22:25:47 +00:00
START_RUNNING ,
2015-04-25 04:39:09 +00:00
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " start-running " , START_RUNNING ) ;
2015-10-02 00:30:14 +00:00
: ( before " End Primitive Recipe Checks " )
2015-05-01 22:25:47 +00:00
case START_RUNNING : {
2015-10-02 00:30:14 +00:00
if ( inst . ingredients . empty ( ) ) {
2016-02-26 21:04:55 +00:00
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'start-running' requires at least one ingredient: the recipe to start running \n " < < end ( ) ;
2015-08-17 02:13:45 +00:00
break ;
}
2015-10-07 07:22:49 +00:00
if ( ! is_mu_recipe ( inst . ingredients . at ( 0 ) ) ) {
2016-05-21 05:09:06 +00:00
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 ( ) ;
2015-08-17 02:13:45 +00:00
break ;
}
2015-10-02 00:30:14 +00:00
break ;
}
: ( before " End Primitive Recipe Implementations " )
case START_RUNNING : {
2015-07-04 02:38:21 +00:00
routine * new_routine = new routine ( ingredients . at ( 0 ) . at ( 0 ) ) ;
2015-05-09 18:35:47 +00:00
new_routine - > parent_index = Current_routine_index ;
2015-05-06 02:39:20 +00:00
// populate ingredients
2016-03-14 03:26:47 +00:00
for ( int i = 1 ; i < SIZE ( current_instruction ( ) . ingredients ) ; + + i ) {
2015-05-13 23:33:40 +00:00
new_routine - > calls . front ( ) . ingredient_atoms . push_back ( ingredients . at ( i ) ) ;
2016-05-06 07:46:39 +00:00
reagent /*copy*/ ingredient = current_instruction ( ) . ingredients . at ( i ) ;
2015-10-28 20:08:26 +00:00
canonize_type ( ingredient ) ;
2015-11-28 06:01:23 +00:00
new_routine - > calls . front ( ) . ingredients . push_back ( ingredient ) ;
2016-08-17 00:03:27 +00:00
// End Populate start-running Ingredient
2015-10-28 20:08:26 +00:00
}
2015-05-06 00:41:10 +00:00
Routines . push_back ( new_routine ) ;
2015-05-07 22:06:53 +00:00
products . resize ( 1 ) ;
2015-05-13 00:00:56 +00:00
products . at ( 0 ) . push_back ( new_routine - > id ) ;
2015-04-25 04:39:09 +00:00
break ;
}
2015-04-25 07:00:49 +00:00
2015-05-02 18:49:11 +00:00
: ( scenario scheduler_runs_single_routine )
% Scheduling_interval = 1 ;
2016-03-08 09:30:14 +00:00
def f1 [
2015-07-28 21:33:22 +00:00
1 : number < - copy 0
2 : number < - copy 0
2015-05-02 18:49:11 +00:00
]
+ schedule : f1
2016-03-21 09:25:52 +00:00
+ run : { 1 : " number " } < - copy { 0 : " literal " }
2015-05-02 18:49:11 +00:00
+ schedule : f1
2016-03-21 09:25:52 +00:00
+ run : { 2 : " number " } < - copy { 0 : " literal " }
2015-05-02 18:49:11 +00:00
2015-04-25 07:00:49 +00:00
: ( scenario scheduler_interleaves_routines )
% Scheduling_interval = 1 ;
2016-03-08 09:30:14 +00:00
def f1 [
2016-01-18 07:15:03 +00:00
start - running f2
2015-07-28 21:33:22 +00:00
1 : number < - copy 0
2 : number < - copy 0
2015-04-25 07:00:49 +00:00
]
2016-03-08 09:30:14 +00:00
def f2 [
2015-07-28 21:33:22 +00:00
3 : number < - copy 0
4 : number < - copy 0
2015-04-25 07:00:49 +00:00
]
+ schedule : f1
2016-03-21 09:25:52 +00:00
+ run : start - running { f2 : " recipe-literal " }
2015-04-25 07:00:49 +00:00
+ schedule : f2
2016-03-21 09:25:52 +00:00
+ run : { 3 : " number " } < - copy { 0 : " literal " }
2015-04-25 07:00:49 +00:00
+ schedule : f1
2016-03-21 09:25:52 +00:00
+ run : { 1 : " number " } < - copy { 0 : " literal " }
2015-04-25 07:00:49 +00:00
+ schedule : f2
2016-03-21 09:25:52 +00:00
+ run : { 4 : " number " } < - copy { 0 : " literal " }
2015-04-25 07:00:49 +00:00
+ schedule : f1
2016-03-21 09:25:52 +00:00
+ run : { 2 : " number " } < - copy { 0 : " literal " }
2015-05-02 18:49:11 +00:00
2015-10-28 20:05:01 +00:00
: ( scenario start_running_takes_ingredients )
2016-03-08 09:30:14 +00:00
def f1 [
2016-01-18 07:15:03 +00:00
start - running f2 , 3
2015-05-10 12:42:56 +00:00
# wait for f2 to run
{
2015-07-28 21:33:22 +00:00
jump - unless 1 : number , - 1
2015-05-10 12:42:56 +00:00
}
2015-05-06 02:39:20 +00:00
]
2016-03-08 09:30:14 +00:00
def f2 [
2015-05-13 17:03:26 +00:00
1 : number < - next - ingredient
2015-07-28 21:33:22 +00:00
2 : number < - add 1 : number , 1
2015-05-06 02:39:20 +00:00
]
+ mem : storing 4 in location 2
2016-08-17 00:03:27 +00:00
//: more complex: refcounting management when starting up new routines
: ( scenario start_running_immediately_updates_refcounts_of_ingredients )
2016-08-17 01:09:53 +00:00
% Scheduling_interval = 1 ;
2016-08-17 00:03:27 +00:00
def main [
local - scope
create - new - routine
2016-08-17 01:09:53 +00:00
# padding to make sure we run new-routine before returning
dummy : number < - copy 0
dummy : number < - copy 0
2016-08-17 00:03:27 +00:00
]
def create - new - routine [
local - scope
n : address : number < - new number : type
* n < - copy 34
start - running new - routine , n
# refcount of n decremented
]
def new - routine n : address : number [
local - scope
load - ingredients
1 : number / raw < - copy * n
]
# check that n wasn't reclaimed when create-new-routine returned
+ mem : storing 34 in location 1
//: to support the previous scenario we'll increment refcounts for all call
//: ingredients right at call time, and stop incrementing refcounts inside
//: next-ingredient
: ( before " End Populate Call Ingredient " )
increment_any_refcounts ( ingredient , ingredients . at ( i ) ) ;
: ( before " End Populate start-running Ingredient " )
increment_any_refcounts ( ingredient , ingredients . at ( i ) ) ;
: ( before " End should_update_refcounts_in_write_memory Special-cases For Primitives " )
2016-08-17 19:48:16 +00:00
if ( inst . operation = = NEXT_INGREDIENT | | inst . operation = = NEXT_INGREDIENT_WITHOUT_TYPECHECKING ) {
if ( space_index ( inst . products . at ( 0 ) ) > 0 ) return true ;
if ( has_property ( inst . products . at ( 0 ) , " raw " ) ) return true ;
2016-08-17 00:03:27 +00:00
return false ;
2016-08-17 19:48:16 +00:00
}
: ( scenario next_ingredient_never_leaks_refcounts )
def create - scope n : address : number - > default - space : address : array : location [
default - space < - new location : type , 2
load - ingredients
]
def use - scope [
local - scope
0 : address : array : location / names : create - scope < - next - ingredient
n : address : number / space : 1 < - next - ingredient # should decrement refcount
* n / space : 1 < - copy 34
n2 : number < - add * n / space : 1 , 1
reply n2
]
def main [
local - scope
n : address : number < - copy 12000 / unsafe # pretend allocation with a known address
* n < - copy 23
scope : address : array : location < - create - scope n
n2 : address : number < - copy 13000 / unsafe
n3 : number < - use - scope scope , n2
]
+ run : { n : ( " address " " number " ) , " space " : " 1 " } < - next - ingredient
+ mem : decrementing refcount of 12000 : 2 - > 1
+ run : { n : ( " address " " number " ) , " space " : " 1 " , " lookup " : ( ) } < - copy { 34 : " literal " }
//: back to testing 'start-running'
2016-08-17 00:03:27 +00:00
2015-05-06 00:41:10 +00:00
: ( scenario start_running_returns_routine_id )
2016-03-08 09:30:14 +00:00
def f1 [
2016-01-18 07:15:03 +00:00
1 : number < - start - running f2
2015-05-06 00:41:10 +00:00
]
2016-03-08 09:30:14 +00:00
def f2 [
2015-07-28 21:33:22 +00:00
12 : number < - copy 44
2015-05-06 00:41:10 +00:00
]
+ mem : storing 2 in location 1
2015-05-27 18:27:50 +00:00
//: this scenario will require some careful setup in escaped C++
//: (straining our tangle capabilities to near-breaking point)
2015-05-02 18:49:11 +00:00
: ( scenario scheduler_skips_completed_routines )
2015-08-20 05:13:15 +00:00
% recipe_ordinal f1 = load ( " recipe f1 [ \n 1:number <- copy 0 \n ] \n " ) . front ( ) ;
% recipe_ordinal f2 = load ( " recipe f2 [ \n 2:number <- copy 0 \n ] \n " ) . front ( ) ;
2015-05-02 18:49:11 +00:00
% Routines . push_back ( new routine ( f1 ) ) ; // f1 meant to run
% Routines . push_back ( new routine ( f2 ) ) ;
% Routines . back ( ) - > state = COMPLETED ; // f2 not meant to run
# must have at least one routine without escaping
2016-03-08 09:30:14 +00:00
def f3 [
2015-07-28 21:33:22 +00:00
3 : number < - copy 0
2015-05-02 18:49:11 +00:00
]
# by interleaving '+' lines with '-' lines, we allow f1 and f3 to run in any order
+ schedule : f1
+ mem : storing 0 in location 1
- schedule : f2
- mem : storing 0 in location 2
+ schedule : f3
+ mem : storing 0 in location 3
: ( scenario scheduler_starts_at_middle_of_routines )
% Routines . push_back ( new routine ( COPY ) ) ;
% Routines . back ( ) - > state = COMPLETED ;
2016-03-08 09:30:14 +00:00
def f1 [
2015-07-28 21:33:22 +00:00
1 : number < - copy 0
2 : number < - copy 0
2015-05-02 18:49:11 +00:00
]
+ schedule : f1
- run : idle
2015-05-06 00:54:46 +00:00
2015-09-12 22:33:21 +00:00
//:: Errors in a routine cause it to terminate.
: ( scenario scheduler_terminates_routines_after_errors )
2015-10-07 05:15:45 +00:00
% Hide_errors = true ;
2015-09-12 22:33:21 +00:00
% Scheduling_interval = 2 ;
2016-03-08 09:30:14 +00:00
def f1 [
2016-01-18 07:15:03 +00:00
start - running f2
2015-09-12 22:33:21 +00:00
1 : number < - copy 0
2 : number < - copy 0
]
2016-03-08 09:30:14 +00:00
def f2 [
2015-09-12 22:33:21 +00:00
# divide by 0 twice
3 : number < - divide - with - remainder 4 , 0
4 : number < - divide - with - remainder 4 , 0
]
# f2 should stop after first divide by 0
2015-10-07 05:15:45 +00:00
+ error : f2 : divide by zero in ' 3 : number < - divide - with - remainder 4 , 0 '
- error : f2 : divide by zero in ' 4 : number < - divide - with - remainder 4 , 0 '
2015-09-12 22:33:21 +00:00
: ( after " operator<<(ostream& os, unused end) " )
2015-10-07 05:15:45 +00:00
if ( Trace_stream & & Trace_stream - > curr_label = = " error " & & Current_routine ) {
2015-09-12 22:33:21 +00:00
Current_routine - > state = COMPLETED ;
}
2015-05-09 18:35:47 +00:00
//:: Routines are marked completed when their parent completes.
2015-05-10 12:42:56 +00:00
: ( scenario scheduler_kills_orphans )
2016-03-08 09:30:14 +00:00
def main [
2016-01-18 07:15:03 +00:00
start - running f1
2015-05-10 12:42:56 +00:00
# f1 never actually runs because its parent completes without waiting for it
]
2016-03-08 09:30:14 +00:00
def f1 [
2015-07-28 21:33:22 +00:00
1 : number < - copy 0
2015-05-10 12:42:56 +00:00
]
- schedule : f1
2015-05-09 18:35:47 +00:00
: ( before " End Scheduler Cleanup " )
2016-03-14 03:26:47 +00:00
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
2015-05-09 18:35:47 +00:00
if ( Routines . at ( i ) - > state = = COMPLETED ) continue ;
if ( Routines . at ( i ) - > parent_index < 0 ) continue ; // root thread
2016-08-16 22:26:52 +00:00
// structured concurrency: http://250bpm.com/blog:71
2015-05-10 12:42:56 +00:00
if ( has_completed_parent ( i ) ) {
Routines . at ( i ) - > state = COMPLETED ;
}
2015-05-09 18:35:47 +00:00
}
: ( code )
2016-03-14 03:26:47 +00:00
bool has_completed_parent ( int routine_index ) {
for ( int j = routine_index ; j > = 0 ; j = Routines . at ( j ) - > parent_index ) {
2015-05-09 18:35:47 +00:00
if ( Routines . at ( j ) - > state = = COMPLETED )
return true ;
}
return false ;
}
2015-05-06 00:54:46 +00:00
//:: 'routine-state' can tell if a given routine id is running
: ( scenario routine_state_test )
% Scheduling_interval = 2 ;
2016-03-08 09:30:14 +00:00
def f1 [
2016-01-18 07:15:03 +00:00
1 : number / child - id < - start - running f2
2015-07-28 21:33:22 +00:00
12 : number < - copy 0 # race condition since we don ' t care about location 12
2015-05-06 00:54:46 +00:00
# thanks to Scheduling_interval, f2's one instruction runs in between here and completes
2015-05-13 17:03:26 +00:00
2 : number / state < - routine - state 1 : number / child - id
2015-05-06 00:54:46 +00:00
]
2016-03-08 09:30:14 +00:00
def f2 [
2015-07-28 21:33:22 +00:00
12 : number < - copy 0
2015-05-06 00:54:46 +00:00
# trying to run a second instruction marks routine as completed
]
# recipe f2 should be in state COMPLETED
+ mem : storing 1 in location 2
: ( before " End Primitive Recipe Declarations " )
ROUTINE_STATE ,
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " routine-state " , ROUTINE_STATE ) ;
2015-10-02 00:30:14 +00:00
: ( before " End Primitive Recipe Checks " )
2015-05-06 00:54:46 +00:00
case ROUTINE_STATE : {
2015-10-02 00:30:14 +00:00
if ( SIZE ( inst . ingredients ) ! = 1 ) {
2016-07-22 02:22:03 +00:00
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'routine-state' requires exactly one ingredient, but got ' " < < inst . original_string < < " ' \n " < < end ( ) ;
2015-07-25 21:19:28 +00:00
break ;
}
2015-10-07 07:22:49 +00:00
if ( ! is_mu_number ( inst . ingredients . at ( 0 ) ) ) {
2016-05-21 05:09:06 +00:00
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 ( ) ;
2015-07-25 21:19:28 +00:00
break ;
}
2015-10-02 00:30:14 +00:00
break ;
}
: ( before " End Primitive Recipe Implementations " )
case ROUTINE_STATE : {
2016-03-14 03:26:47 +00:00
int id = ingredients . at ( 0 ) . at ( 0 ) ;
int result = - 1 ;
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
2015-05-07 22:49:40 +00:00
if ( Routines . at ( i ) - > id = = id ) {
result = Routines . at ( i ) - > state ;
2015-05-06 00:54:46 +00:00
break ;
}
}
2015-08-03 05:18:19 +00:00
products . resize ( 1 ) ;
2015-05-13 00:00:56 +00:00
products . at ( 0 ) . push_back ( result ) ;
2015-05-06 00:54:46 +00:00
break ;
}
2015-05-08 01:35:33 +00:00
2015-05-09 18:35:47 +00:00
//:: miscellaneous helpers
2015-05-10 13:02:36 +00:00
: ( before " End Primitive Recipe Declarations " )
STOP ,
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " stop " , STOP ) ;
2015-10-02 00:30:14 +00:00
: ( before " End Primitive Recipe Checks " )
2015-05-10 13:02:36 +00:00
case STOP : {
2015-10-02 00:30:14 +00:00
if ( SIZE ( inst . ingredients ) ! = 1 ) {
2016-07-22 02:22:03 +00:00
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'stop' requires exactly one ingredient, but got ' " < < inst . original_string < < " ' \n " < < end ( ) ;
2015-07-25 21:19:28 +00:00
break ;
}
2015-10-07 07:22:49 +00:00
if ( ! is_mu_number ( inst . ingredients . at ( 0 ) ) ) {
2016-05-21 05:09:06 +00:00
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 ( ) ;
2015-07-25 21:19:28 +00:00
break ;
}
2015-10-02 00:30:14 +00:00
break ;
}
: ( before " End Primitive Recipe Implementations " )
case STOP : {
2016-03-14 03:26:47 +00:00
int id = ingredients . at ( 0 ) . at ( 0 ) ;
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
2015-05-10 13:02:36 +00:00
if ( Routines . at ( i ) - > id = = id ) {
Routines . at ( i ) - > state = COMPLETED ;
break ;
}
}
break ;
}
2015-05-08 15:21:36 +00:00
: ( before " End Primitive Recipe Declarations " )
_DUMP_ROUTINES ,
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " $dump-routines " , _DUMP_ROUTINES ) ;
2015-10-02 00:30:14 +00:00
: ( before " End Primitive Recipe Checks " )
case _DUMP_ROUTINES : {
break ;
}
2015-05-08 15:21:36 +00:00
: ( before " End Primitive Recipe Implementations " )
case _DUMP_ROUTINES : {
2016-03-14 03:26:47 +00:00
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
2015-05-10 12:42:56 +00:00
cerr < < i < < " : " < < Routines . at ( i ) - > id < < ' ' < < Routines . at ( i ) - > state < < ' ' < < Routines . at ( i ) - > parent_index < < ' \n ' ;
2015-05-08 15:21:36 +00:00
}
break ;
}
2015-08-17 03:12:14 +00:00
//: support for stopping routines after some number of cycles
: ( scenario routine_discontinues_past_limit )
% Scheduling_interval = 2 ;
2016-03-08 09:30:14 +00:00
def f1 [
2016-01-18 07:15:03 +00:00
1 : number / child - id < - start - running f2
2015-09-02 03:10:59 +00:00
limit - time 1 : number / child - id , 10
# padding loop just to make sure f2 has time to completed
2 : number < - copy 20
2 : number < - subtract 2 : number , 1
jump - if 2 : number , - 2 : offset
2015-08-17 03:12:14 +00:00
]
2016-03-08 09:30:14 +00:00
def f2 [
2015-09-02 03:10:59 +00:00
jump - 1 : offset # run forever
$ print [ should never get here ] , 10 / newline
2015-08-17 03:12:14 +00:00
]
# f2 terminates
+ schedule : discontinuing routine 2
: ( before " End routine States " )
DISCONTINUED ,
: ( before " End Scheduler State Transitions " )
if ( Current_routine - > limit > = 0 ) {
if ( Current_routine - > limit < = Scheduling_interval ) {
2015-10-29 18:56:10 +00:00
trace ( 9999 , " schedule " ) < < " discontinuing routine " < < Current_routine - > id < < end ( ) ;
2015-08-17 03:12:14 +00:00
Current_routine - > state = DISCONTINUED ;
Current_routine - > limit = 0 ;
}
else {
Current_routine - > limit - = Scheduling_interval ;
}
}
2016-04-21 02:44:11 +00:00
: ( before " End Test Teardown " )
if ( Passed & & any_routines_with_error ( ) ) {
Passed = false ;
raise < < " some routines died with errors \n " < < end ( ) ;
}
: ( before " End Mu Test Teardown " )
if ( Passed & & any_routines_with_error ( ) ) {
Passed = false ;
raise < < Current_scenario - > name < < " : some routines died with errors \n " < < end ( ) ;
}
: ( code )
bool any_routines_with_error ( ) {
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
if ( Routines . at ( i ) - > state = = DISCONTINUED )
return true ;
}
return false ;
}
2015-08-17 03:12:14 +00:00
: ( before " End routine Fields " )
2016-03-14 03:26:47 +00:00
int limit ;
2015-08-17 03:12:14 +00:00
: ( before " End routine Constructor " )
limit = - 1 ; /* no limit */
: ( before " End Primitive Recipe Declarations " )
LIMIT_TIME ,
: ( before " End Primitive Recipe Numbers " )
2015-11-06 19:06:58 +00:00
put ( Recipe_ordinal , " limit-time " , LIMIT_TIME ) ;
2015-10-02 00:30:14 +00:00
: ( before " End Primitive Recipe Checks " )
2015-08-17 03:12:14 +00:00
case LIMIT_TIME : {
2015-10-02 00:30:14 +00:00
if ( SIZE ( inst . ingredients ) ! = 2 ) {
2016-07-22 02:22:03 +00:00
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'limit-time' requires exactly two ingredient, but got ' " < < inst . original_string < < " ' \n " < < end ( ) ;
2015-08-17 03:12:14 +00:00
break ;
}
2015-10-07 07:22:49 +00:00
if ( ! is_mu_number ( inst . ingredients . at ( 0 ) ) ) {
2016-05-21 05:09:06 +00:00
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 ( ) ;
2015-08-17 03:12:14 +00:00
break ;
}
2015-10-07 07:22:49 +00:00
if ( ! is_mu_number ( inst . ingredients . at ( 1 ) ) ) {
2016-05-21 05:09:06 +00:00
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 ( ) ;
2015-08-17 03:12:14 +00:00
break ;
}
2015-10-02 00:30:14 +00:00
break ;
}
: ( before " End Primitive Recipe Implementations " )
case LIMIT_TIME : {
2016-03-14 03:26:47 +00:00
int id = ingredients . at ( 0 ) . at ( 0 ) ;
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
2015-08-17 03:12:14 +00:00
if ( Routines . at ( i ) - > id = = id ) {
Routines . at ( i ) - > limit = ingredients . at ( 1 ) . at ( 0 ) ;
break ;
}
}
break ;
}
2016-01-18 05:20:05 +00:00
2016-07-07 01:47:37 +00:00
: ( before " End routine Fields " )
int ninstrs ;
: ( before " End routine Constructor " )
ninstrs = 0 ;
: ( after " stop_running_current_routine: " )
2016-07-25 06:25:59 +00:00
Current_routine - > ninstrs + = ninstrs ;
2016-07-07 01:47:37 +00:00
: ( before " End Primitive Recipe Declarations " )
NUMBER_OF_INSTRUCTIONS ,
: ( before " End Primitive Recipe Numbers " )
put ( Recipe_ordinal , " number-of-instructions " , NUMBER_OF_INSTRUCTIONS ) ;
: ( before " End Primitive Recipe Checks " )
case NUMBER_OF_INSTRUCTIONS : {
if ( SIZE ( inst . ingredients ) ! = 1 ) {
2016-07-22 02:22:03 +00:00
raise < < maybe ( get ( Recipe , r ) . name ) < < " 'number-of-instructions' requires exactly one ingredient, but got ' " < < inst . original_string < < " ' \n " < < end ( ) ;
2016-07-07 01:47:37 +00:00
break ;
}
if ( ! is_mu_number ( inst . ingredients . at ( 0 ) ) ) {
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 ( ) ;
break ;
}
break ;
}
: ( before " End Primitive Recipe Implementations " )
case NUMBER_OF_INSTRUCTIONS : {
int id = ingredients . at ( 0 ) . at ( 0 ) ;
int result = - 1 ;
for ( int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
if ( Routines . at ( i ) - > id = = id ) {
result = Routines . at ( i ) - > ninstrs ;
break ;
}
}
products . resize ( 1 ) ;
products . at ( 0 ) . push_back ( result ) ;
break ;
}
2016-07-07 03:31:23 +00:00
2016-07-25 06:25:59 +00:00
: ( scenario number_of_instructions )
2016-03-08 09:30:14 +00:00
def f1 [
2016-07-25 06:25:59 +00:00
10 : number / child - id < - start - running f2
2016-01-18 05:20:05 +00:00
{
2016-07-25 06:25:59 +00:00
loop - unless 20 : number
2016-01-18 05:20:05 +00:00
}
2016-07-25 06:25:59 +00:00
11 : number < - number - of - instructions 10 : number
2016-01-18 05:20:05 +00:00
]
2016-03-08 09:30:14 +00:00
def f2 [
2016-07-25 06:25:59 +00:00
# 2 instructions worth of work
1 : number < - copy 34
20 : number < - copy 1
2016-01-18 05:20:05 +00:00
]
2016-07-25 06:25:59 +00:00
# f2 runs an extra instruction for the implicit return added by the
# fill_in_reply_ingredients transform
+ mem : storing 3 in location 11
2016-07-07 01:47:37 +00:00
2016-07-25 06:25:59 +00:00
: ( scenario number_of_instructions_across_multiple_scheduling_intervals )
2016-07-17 23:50:17 +00:00
% Scheduling_interval = 1 ;
2016-07-07 01:47:37 +00:00
def f1 [
2016-07-07 03:31:23 +00:00
10 : number / child - id < - start - running f2
2016-07-07 01:47:37 +00:00
{
2016-07-07 03:31:23 +00:00
loop - unless 20 : number
2016-07-07 01:47:37 +00:00
}
2016-07-07 03:31:23 +00:00
11 : number < - number - of - instructions 10 : number
2016-07-07 01:47:37 +00:00
]
def f2 [
2016-07-17 23:50:17 +00:00
# 4 instructions worth of work
1 : number < - copy 34
2 : number < - copy 1
2 : number < - copy 3
2016-07-07 03:31:23 +00:00
20 : number < - copy 1
2016-07-07 01:47:37 +00:00
]
2016-07-07 03:31:23 +00:00
# f2 runs an extra instruction for the implicit return added by the
# fill_in_reply_ingredients transform
2016-07-17 23:50:17 +00:00
+ mem : storing 5 in location 11
2016-07-25 06:25:59 +00:00
//:: make sure that each routine gets a different alloc to start
: ( scenario new_concurrent )
def f1 [
start - running f2
1 : address : number / raw < - new number : type
# wait for f2 to complete
{
loop - unless 4 : number / raw
}
]
def f2 [
2 : address : number / raw < - new number : type
# hack: assumes scheduler implementation
3 : boolean / raw < - equal 1 : address : number / raw , 2 : address : number / raw
# signal f2 complete
4 : number / raw < - copy 1
]
+ mem : storing 0 in location 3