blob: d2c939b69be2a6a9eea7d0002411e176f6e0e75c [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001use std::future::Future;
2use std::pin::Pin;
3use std::task::Waker;
4use std::task::{Context, Poll};
5use std::thread;
6use std::time::Duration;
7
8use async_task::Task;
9use crossbeam::atomic::AtomicCell;
10use crossbeam::channel;
11use lazy_static::lazy_static;
12
13// Creates a future with event counters.
14//
15// Usage: `future!(f, waker, POLL, DROP)`
16//
17// The future `f` always sleeps for 200 ms and returns `Poll::Pending`.
18// When it gets polled, `POLL` is incremented.
19// When it gets dropped, `DROP` is incremented.
20//
21// Every time the future is run, it stores the waker into a global variable.
22// This waker can be extracted using the `waker` function.
23macro_rules! future {
24 ($name:pat, $waker:pat, $poll:ident, $drop:ident) => {
25 lazy_static! {
26 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
27 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
28 static ref WAKER: AtomicCell<Option<Waker>> = AtomicCell::new(None);
29 }
30
31 let ($name, $waker) = {
32 struct Fut(Box<i32>);
33
34 impl Future for Fut {
35 type Output = ();
36
37 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
38 WAKER.store(Some(cx.waker().clone()));
39 $poll.fetch_add(1);
40 thread::sleep(ms(200));
41 Poll::Pending
42 }
43 }
44
45 impl Drop for Fut {
46 fn drop(&mut self) {
47 $drop.fetch_add(1);
48 }
49 }
50
51 (Fut(Box::new(0)), || WAKER.swap(None).unwrap())
52 };
53 };
54}
55
56// Creates a schedule function with event counters.
57//
58// Usage: `schedule!(s, chan, SCHED, DROP)`
59//
60// The schedule function `s` pushes the task into `chan`.
61// When it gets invoked, `SCHED` is incremented.
62// When it gets dropped, `DROP` is incremented.
63//
64// Receiver `chan` extracts the task when it is scheduled.
65macro_rules! schedule {
66 ($name:pat, $chan:pat, $sched:ident, $drop:ident) => {
67 lazy_static! {
68 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
69 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
70 }
71
72 let ($name, $chan) = {
73 let (s, r) = channel::unbounded();
74
75 struct Guard(Box<i32>);
76
77 impl Drop for Guard {
78 fn drop(&mut self) {
79 $drop.fetch_add(1);
80 }
81 }
82
83 let guard = Guard(Box::new(0));
84 let sched = move |task: Task<_>| {
85 &guard;
86 $sched.fetch_add(1);
87 s.send(task).unwrap();
88 };
89
90 (sched, r)
91 };
92 };
93}
94
95// Creates a task with event counters.
96//
97// Usage: `task!(task, handle f, s, DROP)`
98//
99// A task with future `f` and schedule function `s` is created.
100// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
101// When the tag inside the task gets dropped, `DROP` is incremented.
102macro_rules! task {
103 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
104 lazy_static! {
105 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
106 }
107
108 let ($task, $handle) = {
109 struct Tag(Box<i32>);
110
111 impl Drop for Tag {
112 fn drop(&mut self) {
113 $drop.fetch_add(1);
114 }
115 }
116
117 async_task::spawn($future, $schedule, Tag(Box::new(0)))
118 };
119 };
120}
121
122fn ms(ms: u64) -> Duration {
123 Duration::from_millis(ms)
124}
125
126#[test]
127fn wake_during_run() {
128 future!(f, waker, POLL, DROP_F);
129 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100130 task!(task, _handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200131
132 task.run();
133 let w = waker();
134 w.wake_by_ref();
135 let task = chan.recv().unwrap();
136
137 crossbeam::scope(|scope| {
138 scope.spawn(|_| {
139 task.run();
140 assert_eq!(POLL.load(), 2);
141 assert_eq!(SCHEDULE.load(), 2);
142 assert_eq!(DROP_F.load(), 0);
143 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100144 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200145 assert_eq!(chan.len(), 1);
146 });
147
148 thread::sleep(ms(100));
149
150 w.wake_by_ref();
151 assert_eq!(POLL.load(), 2);
152 assert_eq!(SCHEDULE.load(), 1);
153 assert_eq!(DROP_F.load(), 0);
154 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100155 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200156 assert_eq!(chan.len(), 0);
157
158 thread::sleep(ms(200));
159
160 assert_eq!(POLL.load(), 2);
161 assert_eq!(SCHEDULE.load(), 2);
162 assert_eq!(DROP_F.load(), 0);
163 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100164 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200165 assert_eq!(chan.len(), 1);
166 })
167 .unwrap();
168
169 chan.recv().unwrap();
170 drop(waker());
171}
172
173#[test]
174fn cancel_during_run() {
175 future!(f, waker, POLL, DROP_F);
176 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100177 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200178
179 task.run();
180 let w = waker();
181 w.wake();
182 let task = chan.recv().unwrap();
183
184 crossbeam::scope(|scope| {
185 scope.spawn(|_| {
186 task.run();
187 drop(waker());
188 assert_eq!(POLL.load(), 2);
189 assert_eq!(SCHEDULE.load(), 1);
190 assert_eq!(DROP_F.load(), 1);
191 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100192 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200193 assert_eq!(chan.len(), 0);
194 });
195
196 thread::sleep(ms(100));
197
198 handle.cancel();
199 assert_eq!(POLL.load(), 2);
200 assert_eq!(SCHEDULE.load(), 1);
201 assert_eq!(DROP_F.load(), 0);
202 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100203 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200204 assert_eq!(chan.len(), 0);
205
206 drop(handle);
207 assert_eq!(POLL.load(), 2);
208 assert_eq!(SCHEDULE.load(), 1);
209 assert_eq!(DROP_F.load(), 0);
210 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100211 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200212 assert_eq!(chan.len(), 0);
213
214 thread::sleep(ms(200));
215
216 assert_eq!(POLL.load(), 2);
217 assert_eq!(SCHEDULE.load(), 1);
218 assert_eq!(DROP_F.load(), 1);
219 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100220 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200221 assert_eq!(chan.len(), 0);
222 })
223 .unwrap();
224}
225
226#[test]
227fn wake_and_cancel_during_run() {
228 future!(f, waker, POLL, DROP_F);
229 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100230 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200231
232 task.run();
233 let w = waker();
234 w.wake_by_ref();
235 let task = chan.recv().unwrap();
236
237 crossbeam::scope(|scope| {
238 scope.spawn(|_| {
239 task.run();
240 drop(waker());
241 assert_eq!(POLL.load(), 2);
242 assert_eq!(SCHEDULE.load(), 1);
243 assert_eq!(DROP_F.load(), 1);
244 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100245 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200246 assert_eq!(chan.len(), 0);
247 });
248
249 thread::sleep(ms(100));
250
251 w.wake();
252 assert_eq!(POLL.load(), 2);
253 assert_eq!(SCHEDULE.load(), 1);
254 assert_eq!(DROP_F.load(), 0);
255 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100256 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200257 assert_eq!(chan.len(), 0);
258
259 handle.cancel();
260 assert_eq!(POLL.load(), 2);
261 assert_eq!(SCHEDULE.load(), 1);
262 assert_eq!(DROP_F.load(), 0);
263 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100264 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200265 assert_eq!(chan.len(), 0);
266
267 drop(handle);
268 assert_eq!(POLL.load(), 2);
269 assert_eq!(SCHEDULE.load(), 1);
270 assert_eq!(DROP_F.load(), 0);
271 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100272 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200273 assert_eq!(chan.len(), 0);
274
275 thread::sleep(ms(200));
276
277 assert_eq!(POLL.load(), 2);
278 assert_eq!(SCHEDULE.load(), 1);
279 assert_eq!(DROP_F.load(), 1);
280 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100281 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200282 assert_eq!(chan.len(), 0);
283 })
284 .unwrap();
285}
286
287#[test]
288fn cancel_and_wake_during_run() {
289 future!(f, waker, POLL, DROP_F);
290 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100291 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200292
293 task.run();
294 let w = waker();
295 w.wake_by_ref();
296 let task = chan.recv().unwrap();
297
298 crossbeam::scope(|scope| {
299 scope.spawn(|_| {
300 task.run();
301 drop(waker());
302 assert_eq!(POLL.load(), 2);
303 assert_eq!(SCHEDULE.load(), 1);
304 assert_eq!(DROP_F.load(), 1);
305 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100306 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200307 assert_eq!(chan.len(), 0);
308 });
309
310 thread::sleep(ms(100));
311
312 handle.cancel();
313 assert_eq!(POLL.load(), 2);
314 assert_eq!(SCHEDULE.load(), 1);
315 assert_eq!(DROP_F.load(), 0);
316 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100317 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200318 assert_eq!(chan.len(), 0);
319
320 drop(handle);
321 assert_eq!(POLL.load(), 2);
322 assert_eq!(SCHEDULE.load(), 1);
323 assert_eq!(DROP_F.load(), 0);
324 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100325 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200326 assert_eq!(chan.len(), 0);
327
328 w.wake();
329 assert_eq!(POLL.load(), 2);
330 assert_eq!(SCHEDULE.load(), 1);
331 assert_eq!(DROP_F.load(), 0);
332 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100333 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200334 assert_eq!(chan.len(), 0);
335
336 thread::sleep(ms(200));
337
338 assert_eq!(POLL.load(), 2);
339 assert_eq!(SCHEDULE.load(), 1);
340 assert_eq!(DROP_F.load(), 1);
341 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100342 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200343 assert_eq!(chan.len(), 0);
344 })
345 .unwrap();
346}
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200347
348#[test]
349fn drop_last_waker() {
350 future!(f, waker, POLL, DROP_F);
351 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100352 task!(task, handle, f, s, DROP_T);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200353
354 task.run();
355 let w = waker();
356
357 drop(handle);
358 assert_eq!(POLL.load(), 1);
359 assert_eq!(SCHEDULE.load(), 0);
360 assert_eq!(DROP_F.load(), 0);
361 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100362 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200363 assert_eq!(chan.len(), 0);
364
365 drop(w);
366 assert_eq!(POLL.load(), 1);
367 assert_eq!(SCHEDULE.load(), 1);
368 assert_eq!(DROP_F.load(), 0);
369 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100370 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200371 assert_eq!(chan.len(), 1);
372
373 chan.recv().unwrap().run();
374 assert_eq!(POLL.load(), 1);
375 assert_eq!(SCHEDULE.load(), 1);
376 assert_eq!(DROP_F.load(), 1);
377 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100378 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200379 assert_eq!(chan.len(), 0);
380}
381
382#[test]
383fn cancel_last_handle() {
384 future!(f, waker, POLL, DROP_F);
385 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100386 task!(task, handle, f, s, DROP_T);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200387
388 task.run();
389 drop(waker());
390 assert_eq!(POLL.load(), 1);
391 assert_eq!(SCHEDULE.load(), 0);
392 assert_eq!(DROP_F.load(), 0);
393 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100394 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200395 assert_eq!(chan.len(), 0);
396
397 handle.cancel();
398 assert_eq!(POLL.load(), 1);
399 assert_eq!(SCHEDULE.load(), 1);
400 assert_eq!(DROP_F.load(), 0);
401 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100402 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200403 assert_eq!(chan.len(), 1);
404
405 chan.recv().unwrap().run();
406 assert_eq!(POLL.load(), 1);
407 assert_eq!(SCHEDULE.load(), 1);
408 assert_eq!(DROP_F.load(), 1);
409 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100410 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200411 assert_eq!(chan.len(), 0);
412
413 drop(handle);
414 assert_eq!(POLL.load(), 1);
415 assert_eq!(SCHEDULE.load(), 1);
416 assert_eq!(DROP_F.load(), 1);
417 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100418 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200419 assert_eq!(chan.len(), 0);
420}
421
422#[test]
423fn drop_last_handle() {
424 future!(f, waker, POLL, DROP_F);
425 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100426 task!(task, handle, f, s, DROP_T);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200427
428 task.run();
429 drop(waker());
430 assert_eq!(POLL.load(), 1);
431 assert_eq!(SCHEDULE.load(), 0);
432 assert_eq!(DROP_F.load(), 0);
433 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100434 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200435 assert_eq!(chan.len(), 0);
436
437 drop(handle);
438 assert_eq!(POLL.load(), 1);
439 assert_eq!(SCHEDULE.load(), 1);
440 assert_eq!(DROP_F.load(), 0);
441 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100442 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200443 assert_eq!(chan.len(), 1);
444
445 chan.recv().unwrap().run();
446 assert_eq!(POLL.load(), 1);
447 assert_eq!(SCHEDULE.load(), 1);
448 assert_eq!(DROP_F.load(), 1);
449 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100450 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina5c398cf2019-08-20 15:29:43 +0200451 assert_eq!(chan.len(), 0);
452}