blob: 60b7fd6725868c9d4569ad13e6cf8d15eafc18c1 [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001use std::cell::Cell;
2use std::future::Future;
3use std::panic::catch_unwind;
4use std::pin::Pin;
5use std::task::Waker;
6use std::task::{Context, Poll};
7use std::thread;
8use std::time::Duration;
9
10use async_task::Task;
11use crossbeam::atomic::AtomicCell;
12use crossbeam::channel;
Stjepan Glavinafad623a2020-04-14 14:33:37 +020013use futures::future::FutureExt;
Stjepan Glavina1479e862019-08-12 20:18:51 +020014use lazy_static::lazy_static;
15
16// Creates a future with event counters.
17//
18// Usage: `future!(f, waker, POLL, DROP)`
19//
20// The future `f` always sleeps for 200 ms, and panics the second time it is polled.
21// When it gets polled, `POLL` is incremented.
22// When it gets dropped, `DROP` is incremented.
23//
24// Every time the future is run, it stores the waker into a global variable.
25// This waker can be extracted using the `waker` function.
26macro_rules! future {
27 ($name:pat, $waker:pat, $poll:ident, $drop:ident) => {
28 lazy_static! {
29 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
30 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
31 static ref WAKER: AtomicCell<Option<Waker>> = AtomicCell::new(None);
32 }
33
34 let ($name, $waker) = {
35 struct Fut(Cell<bool>, Box<i32>);
36
37 impl Future for Fut {
38 type Output = ();
39
40 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
41 WAKER.store(Some(cx.waker().clone()));
42 $poll.fetch_add(1);
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +010043 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +020044
45 if self.0.get() {
46 panic!()
47 } else {
48 self.0.set(true);
49 Poll::Pending
50 }
51 }
52 }
53
54 impl Drop for Fut {
55 fn drop(&mut self) {
56 $drop.fetch_add(1);
57 }
58 }
59
60 (Fut(Cell::new(false), Box::new(0)), || {
61 WAKER.swap(None).unwrap()
62 })
63 };
64 };
65}
66
67// Creates a schedule function with event counters.
68//
69// Usage: `schedule!(s, chan, SCHED, DROP)`
70//
71// The schedule function `s` pushes the task into `chan`.
72// When it gets invoked, `SCHED` is incremented.
73// When it gets dropped, `DROP` is incremented.
74//
75// Receiver `chan` extracts the task when it is scheduled.
76macro_rules! schedule {
77 ($name:pat, $chan:pat, $sched:ident, $drop:ident) => {
78 lazy_static! {
79 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
80 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
81 }
82
83 let ($name, $chan) = {
84 let (s, r) = channel::unbounded();
85
86 struct Guard(Box<i32>);
87
88 impl Drop for Guard {
89 fn drop(&mut self) {
90 $drop.fetch_add(1);
91 }
92 }
93
94 let guard = Guard(Box::new(0));
95 let sched = move |task: Task<_>| {
96 &guard;
97 $sched.fetch_add(1);
98 s.send(task).unwrap();
99 };
100
101 (sched, r)
102 };
103 };
104}
105
106// Creates a task with event counters.
107//
108// Usage: `task!(task, handle f, s, DROP)`
109//
110// A task with future `f` and schedule function `s` is created.
111// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
112// When the tag inside the task gets dropped, `DROP` is incremented.
113macro_rules! task {
114 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
115 lazy_static! {
116 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
117 }
118
119 let ($task, $handle) = {
120 struct Tag(Box<i32>);
121
122 impl Drop for Tag {
123 fn drop(&mut self) {
124 $drop.fetch_add(1);
125 }
126 }
127
128 async_task::spawn($future, $schedule, Tag(Box::new(0)))
129 };
130 };
131}
132
133fn ms(ms: u64) -> Duration {
134 Duration::from_millis(ms)
135}
136
137#[test]
138fn wake_during_run() {
139 future!(f, waker, POLL, DROP_F);
140 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100141 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200142
143 task.run();
144 let w = waker();
145 w.wake_by_ref();
146 let task = chan.recv().unwrap();
147
148 crossbeam::scope(|scope| {
149 scope.spawn(|_| {
150 assert!(catch_unwind(|| task.run()).is_err());
151 drop(waker());
152 assert_eq!(POLL.load(), 2);
153 assert_eq!(SCHEDULE.load(), 1);
154 assert_eq!(DROP_F.load(), 1);
155 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100156 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200157 assert_eq!(chan.len(), 0);
158 });
159
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100160 thread::sleep(ms(200));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200161
162 w.wake();
163 drop(handle);
164 assert_eq!(POLL.load(), 2);
165 assert_eq!(SCHEDULE.load(), 1);
166 assert_eq!(DROP_F.load(), 0);
167 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100168 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200169 assert_eq!(chan.len(), 0);
170
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100171 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200172
173 assert_eq!(POLL.load(), 2);
174 assert_eq!(SCHEDULE.load(), 1);
175 assert_eq!(DROP_F.load(), 1);
176 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100177 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200178 assert_eq!(chan.len(), 0);
179 })
180 .unwrap();
181}
182
183#[test]
184fn cancel_during_run() {
185 future!(f, waker, POLL, DROP_F);
186 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100187 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200188
189 task.run();
190 let w = waker();
191 w.wake();
192 let task = chan.recv().unwrap();
193
194 crossbeam::scope(|scope| {
195 scope.spawn(|_| {
196 assert!(catch_unwind(|| task.run()).is_err());
197 drop(waker());
198 assert_eq!(POLL.load(), 2);
199 assert_eq!(SCHEDULE.load(), 1);
200 assert_eq!(DROP_F.load(), 1);
201 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100202 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200203 assert_eq!(chan.len(), 0);
204 });
205
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100206 thread::sleep(ms(200));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200207
208 handle.cancel();
209 assert_eq!(POLL.load(), 2);
210 assert_eq!(SCHEDULE.load(), 1);
211 assert_eq!(DROP_F.load(), 0);
212 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100213 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200214 assert_eq!(chan.len(), 0);
215
216 drop(handle);
217 assert_eq!(POLL.load(), 2);
218 assert_eq!(SCHEDULE.load(), 1);
219 assert_eq!(DROP_F.load(), 0);
220 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100221 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200222 assert_eq!(chan.len(), 0);
223
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100224 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200225
226 assert_eq!(POLL.load(), 2);
227 assert_eq!(SCHEDULE.load(), 1);
228 assert_eq!(DROP_F.load(), 1);
229 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100230 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200231 assert_eq!(chan.len(), 0);
232 })
233 .unwrap();
234}
235
236#[test]
237fn wake_and_cancel_during_run() {
238 future!(f, waker, POLL, DROP_F);
239 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100240 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200241
242 task.run();
243 let w = waker();
244 w.wake_by_ref();
245 let task = chan.recv().unwrap();
246
247 crossbeam::scope(|scope| {
248 scope.spawn(|_| {
249 assert!(catch_unwind(|| task.run()).is_err());
250 drop(waker());
251 assert_eq!(POLL.load(), 2);
252 assert_eq!(SCHEDULE.load(), 1);
253 assert_eq!(DROP_F.load(), 1);
254 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100255 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200256 assert_eq!(chan.len(), 0);
257 });
258
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100259 thread::sleep(ms(200));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200260
261 w.wake();
262 assert_eq!(POLL.load(), 2);
263 assert_eq!(SCHEDULE.load(), 1);
264 assert_eq!(DROP_F.load(), 0);
265 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100266 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200267 assert_eq!(chan.len(), 0);
268
269 handle.cancel();
270 assert_eq!(POLL.load(), 2);
271 assert_eq!(SCHEDULE.load(), 1);
272 assert_eq!(DROP_F.load(), 0);
273 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100274 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200275 assert_eq!(chan.len(), 0);
276
277 drop(handle);
278 assert_eq!(POLL.load(), 2);
279 assert_eq!(SCHEDULE.load(), 1);
280 assert_eq!(DROP_F.load(), 0);
281 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100282 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200283 assert_eq!(chan.len(), 0);
284
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100285 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200286
287 assert_eq!(POLL.load(), 2);
288 assert_eq!(SCHEDULE.load(), 1);
289 assert_eq!(DROP_F.load(), 1);
290 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100291 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200292 assert_eq!(chan.len(), 0);
293 })
294 .unwrap();
295}
296
297#[test]
298fn cancel_and_wake_during_run() {
299 future!(f, waker, POLL, DROP_F);
300 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100301 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200302
303 task.run();
304 let w = waker();
305 w.wake_by_ref();
306 let task = chan.recv().unwrap();
307
308 crossbeam::scope(|scope| {
309 scope.spawn(|_| {
310 assert!(catch_unwind(|| task.run()).is_err());
311 drop(waker());
312 assert_eq!(POLL.load(), 2);
313 assert_eq!(SCHEDULE.load(), 1);
314 assert_eq!(DROP_F.load(), 1);
315 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100316 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200317 assert_eq!(chan.len(), 0);
318 });
319
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100320 thread::sleep(ms(200));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200321
322 handle.cancel();
323 assert_eq!(POLL.load(), 2);
324 assert_eq!(SCHEDULE.load(), 1);
325 assert_eq!(DROP_F.load(), 0);
326 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100327 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200328 assert_eq!(chan.len(), 0);
329
330 drop(handle);
331 assert_eq!(POLL.load(), 2);
332 assert_eq!(SCHEDULE.load(), 1);
333 assert_eq!(DROP_F.load(), 0);
334 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100335 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200336 assert_eq!(chan.len(), 0);
337
338 w.wake();
339 assert_eq!(POLL.load(), 2);
340 assert_eq!(SCHEDULE.load(), 1);
341 assert_eq!(DROP_F.load(), 0);
342 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100343 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200344 assert_eq!(chan.len(), 0);
345
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100346 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200347
348 assert_eq!(POLL.load(), 2);
349 assert_eq!(SCHEDULE.load(), 1);
350 assert_eq!(DROP_F.load(), 1);
351 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100352 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200353 assert_eq!(chan.len(), 0);
354 })
355 .unwrap();
356}
Stjepan Glavinafad623a2020-04-14 14:33:37 +0200357
358#[test]
359fn panic_and_poll() {
360 future!(f, waker, POLL, DROP_F);
361 schedule!(s, chan, SCHEDULE, DROP_S);
362 task!(task, handle, f, s, DROP_T);
363
364 task.run();
365 waker().wake();
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);
370 assert_eq!(DROP_T.load(), 0);
371
372 let mut handle = handle;
373 assert!((&mut handle).now_or_never().is_none());
374
375 let task = chan.recv().unwrap();
376 assert!(catch_unwind(|| task.run()).is_err());
377 assert_eq!(POLL.load(), 2);
378 assert_eq!(SCHEDULE.load(), 1);
379 assert_eq!(DROP_F.load(), 1);
380 assert_eq!(DROP_S.load(), 0);
381 assert_eq!(DROP_T.load(), 0);
382
383 assert!((&mut handle).now_or_never().is_some());
384 assert_eq!(POLL.load(), 2);
385 assert_eq!(SCHEDULE.load(), 1);
386 assert_eq!(DROP_F.load(), 1);
387 assert_eq!(DROP_S.load(), 0);
388 assert_eq!(DROP_T.load(), 0);
389
390 drop(waker());
391 drop(handle);
392 assert_eq!(POLL.load(), 2);
393 assert_eq!(SCHEDULE.load(), 1);
394 assert_eq!(DROP_F.load(), 1);
395 assert_eq!(DROP_S.load(), 1);
396 assert_eq!(DROP_T.load(), 1);
397}