blob: 82354a537216379a540331bfc085b2769780fa6a (
plain) (
blame)
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
|
//: 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:integer <- copy 0:literal
start-running f2:recipe
wait-for-location 1:integer
# now wait for f2 to run and modify location 1 before using its value
2:integer <- copy 1:integer
]
recipe f2 [
1:integer <- copy 34:literal
]
# 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
index_t 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")
Recipe_number["wait-for-location"] = WAIT_FOR_LOCATION;
:(before "End Primitive Recipe Implementations")
case WAIT_FOR_LOCATION: {
reagent loc = canonize(current_instruction().ingredients.at(0));
Current_routine->state = WAITING;
Current_routine->waiting_on_location = loc.value;
Current_routine->old_value_of_waiting_location = Memory[loc.value];
trace("run") << "waiting for location " << loc.value << " to change from " << Memory[loc.value];
//? trace("schedule") << Current_routine->id << ": waiting for location " << loc.value << " to change from " << Memory[loc.value]; //? 2
break;
}
//: scheduler tweak to get routines out of that state
:(before "End Scheduler State Transitions")
for (index_t i = 0; i < Routines.size(); ++i) {
//? trace("schedule") << "wake up loop 1: routine " << Routines.at(i)->id << " has state " << Routines.at(i)->state; //? 1
if (Routines.at(i)->state != WAITING) continue;
//? trace("schedule") << "waiting on location: " << Routines.at(i)->waiting_on_location; //? 1
//? if (Routines.at(i)->waiting_on_location) //? 2
//? trace("schedule") << "checking routine " << Routines.at(i)->id << " waiting on location " //? 2
//? << Routines.at(i)->waiting_on_location << ": " << Memory[Routines.at(i)->waiting_on_location] << " vs " << Routines.at(i)->old_value_of_waiting_location; //? 2
if (Routines.at(i)->waiting_on_location &&
Memory[Routines.at(i)->waiting_on_location] != Routines.at(i)->old_value_of_waiting_location) {
trace("schedule") << "waking up routine\n";
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:integer <- copy 0:literal
12:integer/routine <- start-running f2:recipe
wait-for-routine 12:integer/routine
# now wait for f2 to run and modify location 1 before using its value
3:integer <- copy 1:integer
]
recipe f2 [
1:integer <- copy 34:literal
]
+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
index_t waiting_on_routine;
:(before "End routine Constructor")
waiting_on_routine = 0;
:(before "End Primitive Recipe Declarations")
WAIT_FOR_ROUTINE,
:(before "End Primitive Recipe Numbers")
Recipe_number["wait-for-routine"] = WAIT_FOR_ROUTINE;
:(before "End Primitive Recipe Implementations")
case WAIT_FOR_ROUTINE: {
Current_routine->state = WAITING;
assert(ingredients.at(0).size() == 1); // scalar
Current_routine->waiting_on_routine = ingredients.at(0).at(0);
trace("run") << "waiting for routine " << ingredients.at(0).at(0);
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 (index_t i = 0; i < Routines.size(); ++i) {
if (Routines.at(i)->state != WAITING) continue;
if (!Routines.at(i)->waiting_on_routine) continue;
index_t id = Routines.at(i)->waiting_on_routine;
assert(id != Routines.at(i)->id);
for (index_t j = 0; j < Routines.size(); ++j) {
if (Routines.at(j)->id == id && Routines.at(j)->state != RUNNING) {
trace("schedule") << "waking up routine " << Routines.at(i)->id;
Routines.at(i)->state = RUNNING;
Routines.at(i)->waiting_on_routine = 0;
}
}
}
:(before "End Primitive Recipe Declarations")
SWITCH,
:(before "End Primitive Recipe Numbers")
Recipe_number["switch"] = SWITCH;
:(before "End Primitive Recipe Implementations")
case SWITCH: {
index_t id = some_other_running_routine();
if (id) {
assert(id != Current_routine->id);
//? cerr << "waiting on " << id << " from " << Current_routine->id << '\n'; //? 1
Current_routine->state = WAITING;
Current_routine->waiting_on_routine = id;
}
break;
}
:(code)
index_t some_other_running_routine() {
for (index_t i = 0; i < Routines.size(); ++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;
}
|