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 )
2015-04-25 04:39:09 +00:00
recipe f1 [
2015-05-01 22:25:47 +00:00
start - running f2 : recipe
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
]
recipe 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() " )
2015-05-17 09:22:41 +00:00
void run_current_routine ( long long int time_slice )
: ( replace " while (!Current_routine->completed()) " following " void run_current_routine(long long int time_slice) " )
long long 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 ;
2015-05-17 09:22:41 +00:00
long long int Current_routine_index = 0 ;
long long 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-04-27 05:44:47 +00:00
Routines . push_back ( new routine ( r ) ) ;
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-07-25 07:02:20 +00:00
trace ( " schedule " ) < < current_routine_label ( ) < < end ( ) ;
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
}
}
: ( code )
bool all_routines_done ( ) {
2015-05-17 09:22:41 +00:00
for ( long long int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
2015-05-07 22:49:40 +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 ) ) ;
for ( long long 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 ;
call_stack calls = Current_routine - > calls ;
for ( call_stack : : iterator p = calls . begin ( ) ; p ! = calls . end ( ) ; + + p ) {
if ( p ! = calls . begin ( ) ) result < < ' / ' ;
result < < Recipe [ p - > running_recipe ] . name ;
}
return result . str ( ) ;
}
2015-04-25 04:39:09 +00:00
: ( before " End Teardown " )
2015-05-17 09:22:41 +00:00
for ( long long 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-04-25 04:39:09 +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 " )
2015-05-17 09:22:41 +00:00
long long int id ;
2015-05-06 00:41:10 +00:00
: ( before " End Globals " )
2015-05-17 09:22:41 +00:00
long long 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.
long long int parent_index ; // only < 0 if there's no parent_index
: ( 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-07-04 16:40:50 +00:00
Recipe_ordinal [ " start-running " ] = START_RUNNING ;
2015-04-25 04:39:09 +00:00
: ( before " End Primitive Recipe Implementations " )
2015-05-01 22:25:47 +00:00
case START_RUNNING : {
2015-08-17 02:13:45 +00:00
if ( ingredients . empty ( ) ) {
raise < < " 'start-running' requires at least one ingredient: the recipe to start running \n " < < end ( ) ;
break ;
}
if ( ! scalar ( ingredients . at ( 0 ) ) ) {
raise < < " first ingredient of 'start-running' should be a recipe, but got " < < current_instruction ( ) . ingredients . at ( 0 ) . original_string < < ' \n ' < < end ( ) ;
break ;
}
if ( ! ingredients . at ( 0 ) . at ( 0 ) ) {
raise < < " 'start-running' received non-existent recipe: ' " < < current_instruction ( ) . to_string ( ) < < " ' \n " < < end ( ) ;
break ;
}
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
2015-05-17 09:22:41 +00:00
for ( long long 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 ) ) ;
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 ;
recipe 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
2015-07-28 21:33:22 +00:00
+ run : 1 : number < - copy 0
2015-05-02 18:49:11 +00:00
+ schedule : f1
2015-07-28 21:33:22 +00:00
+ run : 2 : number < - copy 0
2015-05-02 18:49:11 +00:00
2015-04-25 07:00:49 +00:00
: ( scenario scheduler_interleaves_routines )
% Scheduling_interval = 1 ;
recipe f1 [
2015-05-01 22:25:47 +00:00
start - running f2 : recipe
2015-07-28 21:33:22 +00:00
1 : number < - copy 0
2 : number < - copy 0
2015-04-25 07:00:49 +00:00
]
recipe 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
2015-05-21 19:36:59 +00:00
+ run : start - running f2 : recipe
2015-04-25 07:00:49 +00:00
+ schedule : f2
2015-07-28 21:33:22 +00:00
+ run : 3 : number < - copy 0
2015-04-25 07:00:49 +00:00
+ schedule : f1
2015-07-28 21:33:22 +00:00
+ run : 1 : number < - copy 0
2015-04-25 07:00:49 +00:00
+ schedule : f2
2015-07-28 21:33:22 +00:00
+ run : 4 : number < - copy 0
2015-04-25 07:00:49 +00:00
+ schedule : f1
2015-07-28 21:33:22 +00:00
+ run : 2 : number < - copy 0
2015-05-02 18:49:11 +00:00
2015-05-06 02:39:20 +00:00
: ( scenario start_running_takes_args )
recipe f1 [
2015-07-28 21:33:22 +00:00
start - running f2 : recipe , 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
]
recipe 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
2015-05-06 00:41:10 +00:00
: ( scenario start_running_returns_routine_id )
recipe f1 [
2015-05-13 17:03:26 +00:00
1 : number < - start - running f2 : recipe
2015-05-06 00:41:10 +00:00
]
recipe 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-07-28 21:33:22 +00:00
% recipe_ordinal f1 = load ( " recipe f1 [ \n 1:number <- copy 0 \n ] " ) . front ( ) ;
% recipe_ordinal f2 = load ( " recipe f2 [ \n 2:number <- copy 0 \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
# ? % Trace_stream->dump_layer = "all";
# must have at least one routine without escaping
recipe 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 ;
recipe 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-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 )
recipe main [
start - running f1 : recipe
# f1 never actually runs because its parent completes without waiting for it
]
recipe 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 " )
2015-05-17 09:22:41 +00:00
for ( long long 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
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 )
2015-05-17 09:22:41 +00:00
bool has_completed_parent ( long long int routine_index ) {
2015-05-10 12:42:56 +00:00
for ( long long 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 ;
recipe f1 [
2015-05-13 17:03:26 +00:00
1 : number / child - id < - start - running f2 : recipe
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
]
recipe 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-07-04 16:40:50 +00:00
Recipe_ordinal [ " routine-state " ] = ROUTINE_STATE ;
2015-05-06 00:54:46 +00:00
: ( before " End Primitive Recipe Implementations " )
case ROUTINE_STATE : {
2015-07-25 21:19:28 +00:00
if ( SIZE ( ingredients ) ! = 1 ) {
raise < < current_recipe_name ( ) < < " : 'routine-state' requires exactly one ingredient, but got " < < current_instruction ( ) . to_string ( ) < < ' \n ' < < end ( ) ;
break ;
}
if ( ! scalar ( ingredients . at ( 0 ) ) ) {
raise < < current_recipe_name ( ) < < " : first ingredient of 'routine-state' should be a routine id generated by 'start-running', but got " < < current_instruction ( ) . ingredients . at ( 0 ) . original_string < < ' \n ' < < end ( ) ;
break ;
}
2015-05-17 09:22:41 +00:00
long long int id = ingredients . at ( 0 ) . at ( 0 ) ;
2015-05-07 22:06:53 +00:00
long long int result = - 1 ;
2015-05-17 09:22:41 +00:00
for ( long long 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-08 01:35:33 +00:00
: ( before " End Primitive Recipe Declarations " )
RESTART ,
: ( before " End Primitive Recipe Numbers " )
2015-07-04 16:40:50 +00:00
Recipe_ordinal [ " restart " ] = RESTART ;
2015-05-08 01:35:33 +00:00
: ( before " End Primitive Recipe Implementations " )
case RESTART : {
2015-07-25 21:19:28 +00:00
if ( SIZE ( ingredients ) ! = 1 ) {
raise < < current_recipe_name ( ) < < " : 'restart' requires exactly one ingredient, but got " < < current_instruction ( ) . to_string ( ) < < ' \n ' < < end ( ) ;
break ;
}
if ( ! scalar ( ingredients . at ( 0 ) ) ) {
raise < < current_recipe_name ( ) < < " : first ingredient of 'restart' should be a routine id generated by 'start-running', but got " < < current_instruction ( ) . ingredients . at ( 0 ) . original_string < < ' \n ' < < end ( ) ;
break ;
}
2015-05-17 09:22:41 +00:00
long long int id = ingredients . at ( 0 ) . at ( 0 ) ;
for ( long long int i = 0 ; i < SIZE ( Routines ) ; + + i ) {
2015-05-08 01:35:33 +00:00
if ( Routines . at ( i ) - > id = = id ) {
Routines . at ( i ) - > state = RUNNING ;
break ;
}
}
break ;
}
2015-05-08 15:21:36 +00:00
2015-05-10 13:02:36 +00:00
: ( before " End Primitive Recipe Declarations " )
STOP ,
: ( before " End Primitive Recipe Numbers " )
2015-07-04 16:40:50 +00:00
Recipe_ordinal [ " stop " ] = STOP ;
2015-05-10 13:02:36 +00:00
: ( before " End Primitive Recipe Implementations " )
case STOP : {
2015-07-25 21:19:28 +00:00
if ( SIZE ( ingredients ) ! = 1 ) {
raise < < current_recipe_name ( ) < < " : 'stop' requires exactly one ingredient, but got " < < current_instruction ( ) . to_string ( ) < < ' \n ' < < end ( ) ;
break ;
}
if ( ! scalar ( ingredients . at ( 0 ) ) ) {
raise < < current_recipe_name ( ) < < " : first ingredient of 'stop' should be a routine id generated by 'start-running', but got " < < current_instruction ( ) . ingredients . at ( 0 ) . original_string < < ' \n ' < < end ( ) ;
break ;
}
2015-05-17 09:22:41 +00:00
long long int id = ingredients . at ( 0 ) . at ( 0 ) ;
for ( long long 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-07-04 16:40:50 +00:00
Recipe_ordinal [ " $dump-routines " ] = _DUMP_ROUTINES ;
2015-05-08 15:21:36 +00:00
: ( before " End Primitive Recipe Implementations " )
case _DUMP_ROUTINES : {
2015-05-17 09:22:41 +00:00
for ( long long 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 ;
}