blob: eb7791217efba12974b8e2991a332dafa180a4b9 [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;
13use lazy_static::lazy_static;
14
15// Creates a future with event counters.
16//
17// Usage: `future!(f, waker, POLL, DROP)`
18//
19// The future `f` always sleeps for 200 ms, and panics the second time it is polled.
20// When it gets polled, `POLL` is incremented.
21// When it gets dropped, `DROP` is incremented.
22//
23// Every time the future is run, it stores the waker into a global variable.
24// This waker can be extracted using the `waker` function.
25macro_rules! future {
26 ($name:pat, $waker:pat, $poll:ident, $drop:ident) => {
27 lazy_static! {
28 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
29 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
30 static ref WAKER: AtomicCell<Option<Waker>> = AtomicCell::new(None);
31 }
32
33 let ($name, $waker) = {
34 struct Fut(Cell<bool>, Box<i32>);
35
36 impl Future for Fut {
37 type Output = ();
38
39 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
40 WAKER.store(Some(cx.waker().clone()));
41 $poll.fetch_add(1);
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +010042 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +020043
44 if self.0.get() {
45 panic!()
46 } else {
47 self.0.set(true);
48 Poll::Pending
49 }
50 }
51 }
52
53 impl Drop for Fut {
54 fn drop(&mut self) {
55 $drop.fetch_add(1);
56 }
57 }
58
59 (Fut(Cell::new(false), Box::new(0)), || {
60 WAKER.swap(None).unwrap()
61 })
62 };
63 };
64}
65
66// Creates a schedule function with event counters.
67//
68// Usage: `schedule!(s, chan, SCHED, DROP)`
69//
70// The schedule function `s` pushes the task into `chan`.
71// When it gets invoked, `SCHED` is incremented.
72// When it gets dropped, `DROP` is incremented.
73//
74// Receiver `chan` extracts the task when it is scheduled.
75macro_rules! schedule {
76 ($name:pat, $chan:pat, $sched:ident, $drop:ident) => {
77 lazy_static! {
78 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
79 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
80 }
81
82 let ($name, $chan) = {
83 let (s, r) = channel::unbounded();
84
85 struct Guard(Box<i32>);
86
87 impl Drop for Guard {
88 fn drop(&mut self) {
89 $drop.fetch_add(1);
90 }
91 }
92
93 let guard = Guard(Box::new(0));
94 let sched = move |task: Task<_>| {
95 &guard;
96 $sched.fetch_add(1);
97 s.send(task).unwrap();
98 };
99
100 (sched, r)
101 };
102 };
103}
104
105// Creates a task with event counters.
106//
107// Usage: `task!(task, handle f, s, DROP)`
108//
109// A task with future `f` and schedule function `s` is created.
110// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
111// When the tag inside the task gets dropped, `DROP` is incremented.
112macro_rules! task {
113 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
114 lazy_static! {
115 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
116 }
117
118 let ($task, $handle) = {
119 struct Tag(Box<i32>);
120
121 impl Drop for Tag {
122 fn drop(&mut self) {
123 $drop.fetch_add(1);
124 }
125 }
126
127 async_task::spawn($future, $schedule, Tag(Box::new(0)))
128 };
129 };
130}
131
132fn ms(ms: u64) -> Duration {
133 Duration::from_millis(ms)
134}
135
136#[test]
137fn wake_during_run() {
138 future!(f, waker, POLL, DROP_F);
139 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100140 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200141
142 task.run();
143 let w = waker();
144 w.wake_by_ref();
145 let task = chan.recv().unwrap();
146
147 crossbeam::scope(|scope| {
148 scope.spawn(|_| {
149 assert!(catch_unwind(|| task.run()).is_err());
150 drop(waker());
151 assert_eq!(POLL.load(), 2);
152 assert_eq!(SCHEDULE.load(), 1);
153 assert_eq!(DROP_F.load(), 1);
154 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100155 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200156 assert_eq!(chan.len(), 0);
157 });
158
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100159 thread::sleep(ms(200));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200160
161 w.wake();
162 drop(handle);
163 assert_eq!(POLL.load(), 2);
164 assert_eq!(SCHEDULE.load(), 1);
165 assert_eq!(DROP_F.load(), 0);
166 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100167 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200168 assert_eq!(chan.len(), 0);
169
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100170 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200171
172 assert_eq!(POLL.load(), 2);
173 assert_eq!(SCHEDULE.load(), 1);
174 assert_eq!(DROP_F.load(), 1);
175 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100176 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200177 assert_eq!(chan.len(), 0);
178 })
179 .unwrap();
180}
181
182#[test]
183fn cancel_during_run() {
184 future!(f, waker, POLL, DROP_F);
185 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100186 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200187
188 task.run();
189 let w = waker();
190 w.wake();
191 let task = chan.recv().unwrap();
192
193 crossbeam::scope(|scope| {
194 scope.spawn(|_| {
195 assert!(catch_unwind(|| task.run()).is_err());
196 drop(waker());
197 assert_eq!(POLL.load(), 2);
198 assert_eq!(SCHEDULE.load(), 1);
199 assert_eq!(DROP_F.load(), 1);
200 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100201 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200202 assert_eq!(chan.len(), 0);
203 });
204
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100205 thread::sleep(ms(200));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200206
207 handle.cancel();
208 assert_eq!(POLL.load(), 2);
209 assert_eq!(SCHEDULE.load(), 1);
210 assert_eq!(DROP_F.load(), 0);
211 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100212 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200213 assert_eq!(chan.len(), 0);
214
215 drop(handle);
216 assert_eq!(POLL.load(), 2);
217 assert_eq!(SCHEDULE.load(), 1);
218 assert_eq!(DROP_F.load(), 0);
219 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100220 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200221 assert_eq!(chan.len(), 0);
222
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100223 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200224
225 assert_eq!(POLL.load(), 2);
226 assert_eq!(SCHEDULE.load(), 1);
227 assert_eq!(DROP_F.load(), 1);
228 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100229 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200230 assert_eq!(chan.len(), 0);
231 })
232 .unwrap();
233}
234
235#[test]
236fn wake_and_cancel_during_run() {
237 future!(f, waker, POLL, DROP_F);
238 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100239 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200240
241 task.run();
242 let w = waker();
243 w.wake_by_ref();
244 let task = chan.recv().unwrap();
245
246 crossbeam::scope(|scope| {
247 scope.spawn(|_| {
248 assert!(catch_unwind(|| task.run()).is_err());
249 drop(waker());
250 assert_eq!(POLL.load(), 2);
251 assert_eq!(SCHEDULE.load(), 1);
252 assert_eq!(DROP_F.load(), 1);
253 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100254 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200255 assert_eq!(chan.len(), 0);
256 });
257
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100258 thread::sleep(ms(200));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200259
260 w.wake();
261 assert_eq!(POLL.load(), 2);
262 assert_eq!(SCHEDULE.load(), 1);
263 assert_eq!(DROP_F.load(), 0);
264 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100265 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200266 assert_eq!(chan.len(), 0);
267
268 handle.cancel();
269 assert_eq!(POLL.load(), 2);
270 assert_eq!(SCHEDULE.load(), 1);
271 assert_eq!(DROP_F.load(), 0);
272 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100273 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200274 assert_eq!(chan.len(), 0);
275
276 drop(handle);
277 assert_eq!(POLL.load(), 2);
278 assert_eq!(SCHEDULE.load(), 1);
279 assert_eq!(DROP_F.load(), 0);
280 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100281 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200282 assert_eq!(chan.len(), 0);
283
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100284 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200285
286 assert_eq!(POLL.load(), 2);
287 assert_eq!(SCHEDULE.load(), 1);
288 assert_eq!(DROP_F.load(), 1);
289 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100290 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200291 assert_eq!(chan.len(), 0);
292 })
293 .unwrap();
294}
295
296#[test]
297fn cancel_and_wake_during_run() {
298 future!(f, waker, POLL, DROP_F);
299 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100300 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200301
302 task.run();
303 let w = waker();
304 w.wake_by_ref();
305 let task = chan.recv().unwrap();
306
307 crossbeam::scope(|scope| {
308 scope.spawn(|_| {
309 assert!(catch_unwind(|| task.run()).is_err());
310 drop(waker());
311 assert_eq!(POLL.load(), 2);
312 assert_eq!(SCHEDULE.load(), 1);
313 assert_eq!(DROP_F.load(), 1);
314 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100315 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200316 assert_eq!(chan.len(), 0);
317 });
318
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100319 thread::sleep(ms(200));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200320
321 handle.cancel();
322 assert_eq!(POLL.load(), 2);
323 assert_eq!(SCHEDULE.load(), 1);
324 assert_eq!(DROP_F.load(), 0);
325 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100326 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200327 assert_eq!(chan.len(), 0);
328
329 drop(handle);
330 assert_eq!(POLL.load(), 2);
331 assert_eq!(SCHEDULE.load(), 1);
332 assert_eq!(DROP_F.load(), 0);
333 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100334 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200335 assert_eq!(chan.len(), 0);
336
337 w.wake();
338 assert_eq!(POLL.load(), 2);
339 assert_eq!(SCHEDULE.load(), 1);
340 assert_eq!(DROP_F.load(), 0);
341 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100342 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200343 assert_eq!(chan.len(), 0);
344
Stjepan Glavinaa94d2f42020-01-25 00:14:33 +0100345 thread::sleep(ms(400));
Stjepan Glavina1479e862019-08-12 20:18:51 +0200346
347 assert_eq!(POLL.load(), 2);
348 assert_eq!(SCHEDULE.load(), 1);
349 assert_eq!(DROP_F.load(), 1);
350 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100351 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200352 assert_eq!(chan.len(), 0);
353 })
354 .unwrap();
355}