1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
//: Routines can be put in a 'waiting' state, from which it will be ready to
//: run again when a specific memory location changes its value. This is mu's
//: basic technique for orchestrating the order in which different routines
//: operate.
:(scenario wait_for_location)
recipe f1 [
1:number <- copy 0
start-running f2
wait-for-location 1:number
# now wait for f2 to run and modify location 1 before using its value
2:number <- copy 1:number
]
recipe f2 [
1:number <- copy 34
]
# if we got the synchronization wrong we'd be storing 0 in location 2
+mem: storing 34 in location 2
//: define the new state that all routines can be in
:(before "End routine States")
WAITING,
:(before "End routine Fields")
// only if state == WAITING
long long int waiting_on_location;
int old_value_of_waiting_location;
:(before "End routine Constructor")
waiting_on_location = old_value_of_waiting_location = 0;
//: primitive recipe to put routines in that state
:(before "End Primitive Recipe Declarations")
WAIT_FOR_LOCATION,
:(before "End Primitive Recipe Numbers")
put(Recipe_ordinal, "wait-for-location", WAIT_FOR_LOCATION);
:(before "End Primitive Recipe Checks")
case WAIT_FOR_LOCATION: {
break;
}
:(before "End Primitive Recipe Implementations")
case WAIT_FOR_LOCATION: {
reagent loc = current_instruction().ingredients.at(0);
canonize(loc);
Current_routine->state = WAITING;
Current_routine->waiting_on_location = loc.value;
Current_routine->old_value_of_waiting_location = get_or_insert(Memory, loc.value);
trace(9998, "run") << "waiting for location " << loc.value << " to change from " << no_scientific(get_or_insert(Memory, loc.value)) << end();
break;
}
//: scheduler tweak to get routines out of that state
:(before "End Scheduler State Transitions")
for (long long int i = 0; i < SIZE(Routines); ++i) {
if (Routines.at(i)->state != WAITING) continue;
if (Routines.at(i)->waiting_on_location &&
get_or_insert(Memory, Routines.at(i)->waiting_on_location) != Routines.at(i)->old_value_of_waiting_location) {
trace(9999, "schedule") << "waking up routine\n" << end();
Routines.at(i)->state = RUNNING;
Routines.at(i)->waiting_on_location = Routines.at(i)->old_value_of_waiting_location = 0;
}
}
//: also allow waiting on a routine to stop running
:(scenario wait_for_routine)
recipe f1 [
1:number <- copy 0
12:number/routine <- start-running f2
wait-for-routine 12:number/routine
# now wait for f2 to run and modify location 1 before using its value
3:number <- copy 1:number
]
recipe f2 [
1:number <- copy 34
]
+schedule: f1
+run: waiting for routine 2
+schedule: f2
+schedule: waking up routine 1
+schedule: f1
# if we got the synchronization wrong we'd be storing 0 in location 3
+mem: storing 34 in location 3
:(before "End routine Fields")
// only if state == WAITING
long long int waiting_on_routine;
:(before "End routine Constructor")
waiting_on_routine = 0;
:(before "End Primitive Recipe Declarations")
WAIT_FOR_ROUTINE,
:(before "End Primitive Recipe Numbers")
put(Recipe_ordinal, "wait-for-routine", WAIT_FOR_ROUTINE);
:(before "End Primitive Recipe Checks")
case WAIT_FOR_ROUTINE: {
if (SIZE(inst.ingredients) != 1) {
raise << maybe(get(Recipe, r).name) << "'wait-for-routine' requires exactly one ingredient, but got " << to_string(inst) << '\n' << end();
break;
}
if (!is_mu_number(inst.ingredients.at(0))) {
raise << maybe(get(Recipe, r).name) << "first ingredient of 'wait-for-routine' 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 WAIT_FOR_ROUTINE: {
if (ingredients.at(0).at(0) == Current_routine->id) {
raise << maybe(current_recipe_name()) << "routine can't wait for itself! " << to_string(current_instruction()) << '\n' << end();
break;
}
Current_routine->state = WAITING;
Current_routine->waiting_on_routine = ingredients.at(0).at(0);
trace(9998, "run") << "waiting for routine " << ingredients.at(0).at(0) << end();
break;
}
:(before "End Scheduler State Transitions")
// Wake up any routines waiting for other routines to go to sleep.
// Important: this must come after the scheduler loop above giving routines
// waiting for locations to change a chance to wake up.
for (long long int i = 0; i < SIZE(Routines); ++i) {
if (Routines.at(i)->state != WAITING) continue;
if (!Routines.at(i)->waiting_on_routine) continue;
long long int id = Routines.at(i)->waiting_on_routine;
assert(id != Routines.at(i)->id); // routine can't wait on itself
for (long long int j = 0; j < SIZE(Routines); ++j) {
if (Routines.at(j)->id == id && Routines.at(j)->state != RUNNING) {
trace(9999, "schedule") << "waking up routine " << Routines.at(i)->id << end();
Routines.at(i)->state = RUNNING;
Routines.at(i)->waiting_on_routine = 0;
}
}
}
:(before "End Primitive Recipe Declarations")
SWITCH,
:(before "End Primitive Recipe Numbers")
put(Recipe_ordinal, "switch", SWITCH);
:(before "End Primitive Recipe Checks")
case SWITCH: {
break;
}
:(before "End Primitive Recipe Implementations")
case SWITCH: {
long long int id = some_other_running_routine();
if (id) {
assert(id != Current_routine->id);
Current_routine->state = WAITING;
Current_routine->waiting_on_routine = id;
}
break;
}
:(code)
long long int some_other_running_routine() {
for (long long int i = 0; i < SIZE(Routines); ++i) {
if (i == Current_routine_index) continue;
assert(Routines.at(i) != Current_routine);
assert(Routines.at(i)->id != Current_routine->id);
if (Routines.at(i)->state == RUNNING)
return Routines.at(i)->id;
}
return 0;
}
|