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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//!
//! Immediate mode is a way to send messages via an output sink without needing to
//! use an async method. If the target supports it, it can 'steal' the thread and
//! run immediately. This is particularly useful for things like logging where
//! having the message appear immediately or blocking until it is processed is a
//! desirable feature.
//!
use *;
use *;
use *;
use ;
use executor;
use *;
use mem;
use ;
use *;
/*
-- send_immediately should steal the thread or wait for it to run until the message being sent is processed so that it can generate back-pressure
-- we do steal the thread at the moment if it's idle but we don't wait if it's running because this can cause deadlocks in some places
-- but to do so we need to make sure we wake everything up that's blocked on a scene when it's being shut down (or the scene can block in this situation)
#[test]
fn park_while_thread_runs() {
use std::thread;
use std::sync::mpsc;
let scene = Arc::new(Scene::default());
// Create some status variables to store the state of the message
let received_message = Arc::new(Mutex::new(0));
let received_immediate = Arc::new(Mutex::new(0));
// The receiver program reads messages in immediate mode and sets the 'received_message' flag as soon as the message is received
let receiver_program = SubProgramId::new();
let receiver_program_counter = Arc::clone(&received_message);
let (start_receiving, wait_for_start) = mpsc::channel::<()>();
let (start_running, wait_for_run) = mpsc::channel::<()>();
scene.add_subprogram(receiver_program,
move |messages: InputStream<()>, context| {
messages.allow_thread_stealing(true);
async move {
let mut messages = messages;
// Block, wait for the other thread to wake us up
println!("Receiver waiting for sender...");
start_running.send(()).unwrap();
wait_for_start.recv().unwrap();
println!("Receiver running");
// Delay to allow the other thread to start sending messages before we call messages.next() (if we await there, it'll just steal the main thread)
thread::sleep(Duration::from_millis(50));
// Increase the counter every time we receive a message
while let Some(_msg) = messages.next().await {
println!("Received message");
assert!(context.current_program_id() == Some(receiver_program), "Context program is {:?}, should be {:?}", context.current_program_id(), receiver_program);
assert!(scene_context().unwrap().current_program_id() == Some(receiver_program), "Thread program is {:?}, should be {:?}", scene_context().unwrap().current_program_id(), receiver_program);
*receiver_program_counter.lock().unwrap() += 1;
}
}
}, 0);
// The sender program sends messages to the receiver in immediate mode
let sender_program = SubProgramId::new();
let receiver_program_counter = Arc::clone(&received_message);
let output_counter = Arc::clone(&received_immediate);
scene.add_subprogram(sender_program,
move |_: InputStream<()>, context| {
let mut message_sender = context.send::<()>(receiver_program).unwrap();
async move {
// Wait for the other thread to start running the first future (so the send_immediate calls will block)
println!("Sender waiting for receiver to start...");
wait_for_run.recv().unwrap();
println!("Sender running");
// Wake it up so it starts processing our messages
start_receiving.send(()).unwrap();
// Send some immediate messages
message_sender.send_immediate(()).unwrap();
message_sender.send_immediate(()).unwrap();
message_sender.send_immediate(()).unwrap();
println!("Sent all messages");
// Store how many have been processed in the output counter
*output_counter.lock().unwrap() = *receiver_program_counter.lock().unwrap();
// Stop the scene once we're done
println!("Stopping");
context.send_message(SceneControl::StopScene).await.unwrap();
}
}, 0);
// Run the scene in two threads (which should pick up both programs)
let mut finished = false;
let thread_scene = Arc::clone(&scene);
let other_thread = thread::spawn(move || {
executor::block_on(async {
thread_scene.run_scene().await;
println!("Thread 2 finished");
});
});
executor::block_on(select(async {
scene.run_scene().await;
println!("Thread 1 finished");
finished = true;
}.boxed(), Delay::new(Duration::from_millis(5000))));
// TODO: the 'stop' doesn't always wake up both threads (so the select doesn't stop until the Delay wakes up)
other_thread.join().unwrap();
// Check it behaved as intended
assert!(*received_immediate.lock().unwrap() == 3, "Expected to have processed 4 messages immediately (processed: {:?})", *received_immediate.lock().unwrap());
assert!(finished, "Scene did not finish");
}
*/