blob: 4bfa7fd5e11c3ccefd4b2c88932cf2ac391949ab [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001use std::cell::Cell;
2use std::future::Future;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5use std::thread;
6use std::time::Duration;
7
8use async_task::Task;
9use crossbeam::atomic::AtomicCell;
10use futures::executor::block_on;
11use futures::future;
12use lazy_static::lazy_static;
13
14// Creates a future with event counters.
15//
16// Usage: `future!(f, POLL, DROP_F, DROP_O)`
17//
18// The future `f` outputs `Poll::Ready`.
19// When it gets polled, `POLL` is incremented.
20// When it gets dropped, `DROP_F` is incremented.
21// When the output gets dropped, `DROP_O` is incremented.
22macro_rules! future {
23 ($name:pat, $poll:ident, $drop_f:ident, $drop_o:ident) => {
24 lazy_static! {
25 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
26 static ref $drop_f: AtomicCell<usize> = AtomicCell::new(0);
27 static ref $drop_o: AtomicCell<usize> = AtomicCell::new(0);
28 }
29
30 let $name = {
31 struct Fut(Box<i32>);
32
33 impl Future for Fut {
34 type Output = Out;
35
36 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
37 $poll.fetch_add(1);
38 Poll::Ready(Out(Box::new(0)))
39 }
40 }
41
42 impl Drop for Fut {
43 fn drop(&mut self) {
44 $drop_f.fetch_add(1);
45 }
46 }
47
48 struct Out(Box<i32>);
49
50 impl Drop for Out {
51 fn drop(&mut self) {
52 $drop_o.fetch_add(1);
53 }
54 }
55
56 Fut(Box::new(0))
57 };
58 };
59}
60
61// Creates a schedule function with event counters.
62//
63// Usage: `schedule!(s, SCHED, DROP)`
64//
65// The schedule function `s` does nothing.
66// When it gets invoked, `SCHED` is incremented.
67// When it gets dropped, `DROP` is incremented.
68macro_rules! schedule {
69 ($name:pat, $sched:ident, $drop:ident) => {
70 lazy_static! {
71 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
72 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
73 }
74
75 let $name = {
76 struct Guard(Box<i32>);
77
78 impl Drop for Guard {
79 fn drop(&mut self) {
80 $drop.fetch_add(1);
81 }
82 }
83
84 let guard = Guard(Box::new(0));
85 move |task: Task<_>| {
86 &guard;
87 task.schedule();
88 $sched.fetch_add(1);
89 }
90 };
91 };
92}
93
94// Creates a task with event counters.
95//
96// Usage: `task!(task, handle f, s, DROP)`
97//
98// A task with future `f` and schedule function `s` is created.
99// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
100// When the tag inside the task gets dropped, `DROP` is incremented.
101macro_rules! task {
102 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
103 lazy_static! {
104 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
105 }
106
107 let ($task, $handle) = {
108 struct Tag(Box<i32>);
109
110 impl Drop for Tag {
111 fn drop(&mut self) {
112 $drop.fetch_add(1);
113 }
114 }
115
116 async_task::spawn($future, $schedule, Tag(Box::new(0)))
117 };
118 };
119}
120
121fn ms(ms: u64) -> Duration {
122 Duration::from_millis(ms)
123}
124
125#[test]
126fn cancel_and_join() {
127 future!(f, POLL, DROP_F, DROP_O);
128 schedule!(s, SCHEDULE, DROP_S);
129 task!(task, handle, f, s, DROP_D);
130
131 assert_eq!(DROP_O.load(), 0);
132
133 task.cancel();
134 drop(task);
135 assert_eq!(DROP_O.load(), 0);
136
137 assert!(block_on(handle).is_none());
138 assert_eq!(POLL.load(), 0);
139 assert_eq!(SCHEDULE.load(), 0);
140 assert_eq!(DROP_F.load(), 1);
141 assert_eq!(DROP_S.load(), 1);
142 assert_eq!(DROP_D.load(), 1);
143 assert_eq!(DROP_O.load(), 0);
144}
145
146#[test]
147fn run_and_join() {
148 future!(f, POLL, DROP_F, DROP_O);
149 schedule!(s, SCHEDULE, DROP_S);
150 task!(task, handle, f, s, DROP_D);
151
152 assert_eq!(DROP_O.load(), 0);
153
154 task.run();
155 assert_eq!(DROP_O.load(), 0);
156
157 assert!(block_on(handle).is_some());
158 assert_eq!(POLL.load(), 1);
159 assert_eq!(SCHEDULE.load(), 0);
160 assert_eq!(DROP_F.load(), 1);
161 assert_eq!(DROP_S.load(), 1);
162 assert_eq!(DROP_D.load(), 1);
163 assert_eq!(DROP_O.load(), 1);
164}
165
166#[test]
167fn drop_handle_and_run() {
168 future!(f, POLL, DROP_F, DROP_O);
169 schedule!(s, SCHEDULE, DROP_S);
170 task!(task, handle, f, s, DROP_D);
171
172 assert_eq!(DROP_O.load(), 0);
173
174 drop(handle);
175 assert_eq!(DROP_O.load(), 0);
176
177 task.run();
178 assert_eq!(POLL.load(), 1);
179 assert_eq!(SCHEDULE.load(), 0);
180 assert_eq!(DROP_F.load(), 1);
181 assert_eq!(DROP_S.load(), 1);
182 assert_eq!(DROP_D.load(), 1);
183 assert_eq!(DROP_O.load(), 1);
184}
185
186#[test]
187fn join_twice() {
188 future!(f, POLL, DROP_F, DROP_O);
189 schedule!(s, SCHEDULE, DROP_S);
190 task!(task, mut handle, f, s, DROP_D);
191
192 assert_eq!(DROP_O.load(), 0);
193
194 task.run();
195 assert_eq!(DROP_O.load(), 0);
196
197 assert!(block_on(&mut handle).is_some());
198 assert_eq!(POLL.load(), 1);
199 assert_eq!(SCHEDULE.load(), 0);
200 assert_eq!(DROP_F.load(), 1);
201 assert_eq!(DROP_S.load(), 0);
202 assert_eq!(DROP_D.load(), 0);
203 assert_eq!(DROP_O.load(), 1);
204
205 assert!(block_on(&mut handle).is_none());
206 assert_eq!(POLL.load(), 1);
207 assert_eq!(SCHEDULE.load(), 0);
208 assert_eq!(DROP_F.load(), 1);
209 assert_eq!(DROP_S.load(), 0);
210 assert_eq!(DROP_D.load(), 0);
211 assert_eq!(DROP_O.load(), 1);
212
213 drop(handle);
214 assert_eq!(DROP_S.load(), 1);
215 assert_eq!(DROP_D.load(), 1);
216}
217
218#[test]
219fn join_and_cancel() {
220 future!(f, POLL, DROP_F, DROP_O);
221 schedule!(s, SCHEDULE, DROP_S);
222 task!(task, handle, f, s, DROP_D);
223
224 crossbeam::scope(|scope| {
225 scope.spawn(|_| {
226 thread::sleep(ms(100));
227
228 task.cancel();
229 drop(task);
230
231 thread::sleep(ms(200));
232 assert_eq!(POLL.load(), 0);
233 assert_eq!(SCHEDULE.load(), 0);
234 assert_eq!(DROP_F.load(), 1);
235 assert_eq!(DROP_O.load(), 0);
236 assert_eq!(DROP_S.load(), 1);
237 assert_eq!(DROP_D.load(), 1);
238 });
239
240 assert!(block_on(handle).is_none());
241 assert_eq!(POLL.load(), 0);
242 assert_eq!(SCHEDULE.load(), 0);
243
244 thread::sleep(ms(100));
245 assert_eq!(DROP_F.load(), 1);
246 assert_eq!(DROP_O.load(), 0);
247 assert_eq!(DROP_S.load(), 1);
248 assert_eq!(DROP_D.load(), 1);
249 })
250 .unwrap();
251}
252
253#[test]
254fn join_and_run() {
255 future!(f, POLL, DROP_F, DROP_O);
256 schedule!(s, SCHEDULE, DROP_S);
257 task!(task, handle, f, s, DROP_D);
258
259 crossbeam::scope(|scope| {
260 scope.spawn(|_| {
261 thread::sleep(ms(200));
262
263 task.run();
264 assert_eq!(POLL.load(), 1);
265 assert_eq!(SCHEDULE.load(), 0);
266 assert_eq!(DROP_F.load(), 1);
267
268 thread::sleep(ms(100));
269 assert_eq!(DROP_S.load(), 1);
270 assert_eq!(DROP_D.load(), 1);
271 });
272
273 assert!(block_on(handle).is_some());
274 assert_eq!(POLL.load(), 1);
275 assert_eq!(SCHEDULE.load(), 0);
276 assert_eq!(DROP_F.load(), 1);
277 assert_eq!(DROP_O.load(), 1);
278
279 thread::sleep(ms(100));
280 assert_eq!(DROP_S.load(), 1);
281 assert_eq!(DROP_D.load(), 1);
282 })
283 .unwrap();
284}
285
286#[test]
287fn try_join_and_run_and_join() {
288 future!(f, POLL, DROP_F, DROP_O);
289 schedule!(s, SCHEDULE, DROP_S);
290 task!(task, mut handle, f, s, DROP_D);
291
292 crossbeam::scope(|scope| {
293 scope.spawn(|_| {
294 thread::sleep(ms(200));
295
296 task.run();
297 assert_eq!(POLL.load(), 1);
298 assert_eq!(SCHEDULE.load(), 0);
299 assert_eq!(DROP_F.load(), 1);
300
301 thread::sleep(ms(100));
302 assert_eq!(DROP_S.load(), 1);
303 assert_eq!(DROP_D.load(), 1);
304 });
305
306 block_on(future::select(&mut handle, future::ready(())));
307 assert_eq!(POLL.load(), 0);
308 assert_eq!(SCHEDULE.load(), 0);
309 assert_eq!(DROP_F.load(), 0);
310 assert_eq!(DROP_S.load(), 0);
311 assert_eq!(DROP_D.load(), 0);
312 assert_eq!(DROP_O.load(), 0);
313
314 assert!(block_on(handle).is_some());
315 assert_eq!(POLL.load(), 1);
316 assert_eq!(SCHEDULE.load(), 0);
317 assert_eq!(DROP_F.load(), 1);
318 assert_eq!(DROP_O.load(), 1);
319
320 thread::sleep(ms(100));
321 assert_eq!(DROP_S.load(), 1);
322 assert_eq!(DROP_D.load(), 1);
323 })
324 .unwrap();
325}
326
327#[test]
328fn try_join_and_cancel_and_run() {
329 future!(f, POLL, DROP_F, DROP_O);
330 schedule!(s, SCHEDULE, DROP_S);
331 task!(task, mut handle, f, s, DROP_D);
332
333 crossbeam::scope(|scope| {
334 scope.spawn(|_| {
335 thread::sleep(ms(200));
336
337 task.run();
338 assert_eq!(POLL.load(), 0);
339 assert_eq!(SCHEDULE.load(), 0);
340 assert_eq!(DROP_F.load(), 1);
341 assert_eq!(DROP_S.load(), 1);
342 assert_eq!(DROP_D.load(), 1);
343 });
344
345 block_on(future::select(&mut handle, future::ready(())));
346 assert_eq!(POLL.load(), 0);
347 assert_eq!(SCHEDULE.load(), 0);
348 assert_eq!(DROP_F.load(), 0);
349 assert_eq!(DROP_S.load(), 0);
350 assert_eq!(DROP_D.load(), 0);
351 assert_eq!(DROP_O.load(), 0);
352
353 handle.cancel();
354 assert_eq!(POLL.load(), 0);
355 assert_eq!(SCHEDULE.load(), 0);
356 assert_eq!(DROP_F.load(), 0);
357 assert_eq!(DROP_S.load(), 0);
358 assert_eq!(DROP_D.load(), 0);
359 assert_eq!(DROP_O.load(), 0);
360
361 drop(handle);
362 assert_eq!(POLL.load(), 0);
363 assert_eq!(SCHEDULE.load(), 0);
364 assert_eq!(DROP_F.load(), 0);
365 assert_eq!(DROP_S.load(), 0);
366 assert_eq!(DROP_D.load(), 0);
367 assert_eq!(DROP_O.load(), 0);
368 })
369 .unwrap();
370}
371
372#[test]
373fn try_join_and_run_and_cancel() {
374 future!(f, POLL, DROP_F, DROP_O);
375 schedule!(s, SCHEDULE, DROP_S);
376 task!(task, mut handle, f, s, DROP_D);
377
378 crossbeam::scope(|scope| {
379 scope.spawn(|_| {
380 thread::sleep(ms(200));
381
382 task.run();
383 assert_eq!(POLL.load(), 1);
384 assert_eq!(SCHEDULE.load(), 0);
385 assert_eq!(DROP_F.load(), 1);
386 assert_eq!(DROP_S.load(), 0);
387 assert_eq!(DROP_D.load(), 0);
388 });
389
390 block_on(future::select(&mut handle, future::ready(())));
391 assert_eq!(POLL.load(), 0);
392 assert_eq!(SCHEDULE.load(), 0);
393 assert_eq!(DROP_F.load(), 0);
394 assert_eq!(DROP_S.load(), 0);
395 assert_eq!(DROP_D.load(), 0);
396 assert_eq!(DROP_O.load(), 0);
397
398 thread::sleep(ms(400));
399
400 handle.cancel();
401 assert_eq!(POLL.load(), 1);
402 assert_eq!(SCHEDULE.load(), 0);
403 assert_eq!(DROP_F.load(), 1);
404 assert_eq!(DROP_S.load(), 0);
405 assert_eq!(DROP_D.load(), 0);
406 assert_eq!(DROP_O.load(), 0);
407
408 drop(handle);
409 assert_eq!(POLL.load(), 1);
410 assert_eq!(SCHEDULE.load(), 0);
411 assert_eq!(DROP_F.load(), 1);
412 assert_eq!(DROP_S.load(), 1);
413 assert_eq!(DROP_D.load(), 1);
414 assert_eq!(DROP_O.load(), 1);
415 })
416 .unwrap();
417}
418
419#[test]
420fn await_output() {
421 struct Fut<T>(Cell<Option<T>>);
422
423 impl<T> Fut<T> {
424 fn new(t: T) -> Fut<T> {
425 Fut(Cell::new(Some(t)))
426 }
427 }
428
429 impl<T> Future for Fut<T> {
430 type Output = T;
431
432 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
433 Poll::Ready(self.0.take().unwrap())
434 }
435 }
436
437 for i in 0..10 {
438 let (task, handle) = async_task::spawn(Fut::new(i), drop, Box::new(0));
439 task.run();
440 assert_eq!(block_on(handle), Some(i));
441 }
442
443 for i in 0..10 {
444 let (task, handle) = async_task::spawn(Fut::new(vec![7; i]), drop, Box::new(0));
445 task.run();
446 assert_eq!(block_on(handle), Some(vec![7; i]));
447 }
448
449 let (task, handle) = async_task::spawn(Fut::new("foo".to_string()), drop, Box::new(0));
450 task.run();
451 assert_eq!(block_on(handle), Some("foo".to_string()));
452}