blob: 0c0a10f4b71319c02014a97f9ce7e9ce72e77a41 [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001use std::future::Future;
2use std::pin::Pin;
3use std::sync::atomic::{AtomicUsize, Ordering};
4use std::task::{Context, Poll};
5
6use async_task::Task;
7use crossbeam::atomic::AtomicCell;
8use crossbeam::channel;
Stjepan Glavinafad623a2020-04-14 14:33:37 +02009use futures::future::{self, FutureExt};
Stjepan Glavina1479e862019-08-12 20:18:51 +020010use lazy_static::lazy_static;
11
12// Creates a future with event counters.
13//
14// Usage: `future!(f, POLL, DROP)`
15//
16// The future `f` always returns `Poll::Ready`.
17// When it gets polled, `POLL` is incremented.
18// When it gets dropped, `DROP` is incremented.
19macro_rules! future {
20 ($name:pat, $poll:ident, $drop:ident) => {
21 lazy_static! {
22 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
23 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
24 }
25
26 let $name = {
27 struct Fut(Box<i32>);
28
29 impl Future for Fut {
30 type Output = Box<i32>;
31
32 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
33 $poll.fetch_add(1);
34 Poll::Ready(Box::new(0))
35 }
36 }
37
38 impl Drop for Fut {
39 fn drop(&mut self) {
40 $drop.fetch_add(1);
41 }
42 }
43
44 Fut(Box::new(0))
45 };
46 };
47}
48
49// Creates a schedule function with event counters.
50//
51// Usage: `schedule!(s, SCHED, DROP)`
52//
53// The schedule function `s` does nothing.
54// When it gets invoked, `SCHED` is incremented.
55// When it gets dropped, `DROP` is incremented.
56macro_rules! schedule {
57 ($name:pat, $sched:ident, $drop:ident) => {
58 lazy_static! {
59 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
60 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
61 }
62
63 let $name = {
64 struct Guard(Box<i32>);
65
66 impl Drop for Guard {
67 fn drop(&mut self) {
68 $drop.fetch_add(1);
69 }
70 }
71
72 let guard = Guard(Box::new(0));
73 move |_task| {
74 &guard;
75 $sched.fetch_add(1);
76 }
77 };
78 };
79}
80
81// Creates a task with event counters.
82//
83// Usage: `task!(task, handle f, s, DROP)`
84//
85// A task with future `f` and schedule function `s` is created.
86// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
87// When the tag inside the task gets dropped, `DROP` is incremented.
88macro_rules! task {
89 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
90 lazy_static! {
91 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
92 }
93
94 let ($task, $handle) = {
95 struct Tag(Box<i32>);
96
97 impl Drop for Tag {
98 fn drop(&mut self) {
99 $drop.fetch_add(1);
100 }
101 }
102
103 async_task::spawn($future, $schedule, Tag(Box::new(0)))
104 };
105 };
106}
107
108#[test]
109fn cancel_and_drop_handle() {
110 future!(f, POLL, DROP_F);
111 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100112 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200113
114 assert_eq!(POLL.load(), 0);
115 assert_eq!(SCHEDULE.load(), 0);
116 assert_eq!(DROP_F.load(), 0);
117 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100118 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200119
120 task.cancel();
121 assert_eq!(POLL.load(), 0);
122 assert_eq!(SCHEDULE.load(), 0);
123 assert_eq!(DROP_F.load(), 0);
124 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100125 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200126
127 drop(handle);
128 assert_eq!(POLL.load(), 0);
129 assert_eq!(SCHEDULE.load(), 0);
130 assert_eq!(DROP_F.load(), 0);
131 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100132 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200133
134 drop(task);
135 assert_eq!(POLL.load(), 0);
136 assert_eq!(SCHEDULE.load(), 0);
137 assert_eq!(DROP_F.load(), 1);
138 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100139 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200140}
141
142#[test]
143fn run_and_drop_handle() {
144 future!(f, POLL, DROP_F);
145 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100146 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200147
148 drop(handle);
149 assert_eq!(POLL.load(), 0);
150 assert_eq!(SCHEDULE.load(), 0);
151 assert_eq!(DROP_F.load(), 0);
152 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100153 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200154
155 task.run();
156 assert_eq!(POLL.load(), 1);
157 assert_eq!(SCHEDULE.load(), 0);
158 assert_eq!(DROP_F.load(), 1);
159 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100160 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200161}
162
163#[test]
164fn drop_handle_and_run() {
165 future!(f, POLL, DROP_F);
166 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100167 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200168
169 drop(handle);
170 assert_eq!(POLL.load(), 0);
171 assert_eq!(SCHEDULE.load(), 0);
172 assert_eq!(DROP_F.load(), 0);
173 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100174 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200175
176 task.run();
177 assert_eq!(POLL.load(), 1);
178 assert_eq!(SCHEDULE.load(), 0);
179 assert_eq!(DROP_F.load(), 1);
180 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100181 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200182}
183
184#[test]
185fn cancel_and_run() {
186 future!(f, POLL, DROP_F);
187 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100188 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200189
190 handle.cancel();
191 assert_eq!(POLL.load(), 0);
192 assert_eq!(SCHEDULE.load(), 0);
193 assert_eq!(DROP_F.load(), 0);
194 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100195 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200196
197 drop(handle);
198 assert_eq!(POLL.load(), 0);
199 assert_eq!(SCHEDULE.load(), 0);
200 assert_eq!(DROP_F.load(), 0);
201 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100202 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200203
204 task.run();
205 assert_eq!(POLL.load(), 0);
206 assert_eq!(SCHEDULE.load(), 0);
207 assert_eq!(DROP_F.load(), 1);
208 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100209 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200210}
211
212#[test]
213fn run_and_cancel() {
214 future!(f, POLL, DROP_F);
215 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100216 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200217
218 task.run();
219 assert_eq!(POLL.load(), 1);
220 assert_eq!(SCHEDULE.load(), 0);
221 assert_eq!(DROP_F.load(), 1);
222 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100223 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200224
225 handle.cancel();
226 assert_eq!(POLL.load(), 1);
227 assert_eq!(SCHEDULE.load(), 0);
228 assert_eq!(DROP_F.load(), 1);
229 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100230 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200231
232 drop(handle);
233 assert_eq!(POLL.load(), 1);
234 assert_eq!(SCHEDULE.load(), 0);
235 assert_eq!(DROP_F.load(), 1);
236 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100237 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200238}
239
240#[test]
Stjepan Glavinafad623a2020-04-14 14:33:37 +0200241fn cancel_and_poll() {
242 future!(f, POLL, DROP_F);
243 schedule!(s, SCHEDULE, DROP_S);
244 task!(task, handle, f, s, DROP_T);
245
246 handle.cancel();
247 assert_eq!(POLL.load(), 0);
248 assert_eq!(SCHEDULE.load(), 0);
249 assert_eq!(DROP_F.load(), 0);
250 assert_eq!(DROP_S.load(), 0);
251 assert_eq!(DROP_T.load(), 0);
252
253 let mut handle = handle;
254 assert!((&mut handle).now_or_never().is_none());
255
256 task.run();
257 assert_eq!(POLL.load(), 0);
258 assert_eq!(SCHEDULE.load(), 0);
259 assert_eq!(DROP_F.load(), 1);
260 assert_eq!(DROP_S.load(), 0);
261 assert_eq!(DROP_T.load(), 0);
262
263 assert!((&mut handle).now_or_never().is_some());
264 assert_eq!(POLL.load(), 0);
265 assert_eq!(SCHEDULE.load(), 0);
266 assert_eq!(DROP_F.load(), 1);
267 assert_eq!(DROP_S.load(), 0);
268 assert_eq!(DROP_T.load(), 0);
269
270 drop(handle);
271 assert_eq!(POLL.load(), 0);
272 assert_eq!(SCHEDULE.load(), 0);
273 assert_eq!(DROP_F.load(), 1);
274 assert_eq!(DROP_S.load(), 1);
275 assert_eq!(DROP_T.load(), 1);
276}
277
278#[test]
Stjepan Glavina1479e862019-08-12 20:18:51 +0200279fn schedule() {
280 let (s, r) = channel::unbounded();
281 let schedule = move |t| s.send(t).unwrap();
282 let (task, _handle) = async_task::spawn(
283 future::poll_fn(|_| Poll::<()>::Pending),
284 schedule,
285 Box::new(0),
286 );
287
288 assert!(r.is_empty());
289 task.schedule();
290
291 let task = r.recv().unwrap();
292 assert!(r.is_empty());
293 task.schedule();
294
295 let task = r.recv().unwrap();
296 assert!(r.is_empty());
297 task.schedule();
298
299 r.recv().unwrap();
300}
301
302#[test]
303fn tag() {
304 let (s, r) = channel::unbounded();
305 let schedule = move |t| s.send(t).unwrap();
306 let (task, handle) = async_task::spawn(
307 future::poll_fn(|_| Poll::<()>::Pending),
308 schedule,
309 AtomicUsize::new(7),
310 );
311
312 assert!(r.is_empty());
313 task.schedule();
314
315 let task = r.recv().unwrap();
316 assert!(r.is_empty());
317 handle.tag().fetch_add(1, Ordering::SeqCst);
318 task.schedule();
319
320 let task = r.recv().unwrap();
321 assert_eq!(task.tag().load(Ordering::SeqCst), 8);
322 assert!(r.is_empty());
323 task.schedule();
324
325 r.recv().unwrap();
326}
327
328#[test]
329fn schedule_counter() {
330 let (s, r) = channel::unbounded();
331 let schedule = move |t: Task<AtomicUsize>| {
332 t.tag().fetch_add(1, Ordering::SeqCst);
333 s.send(t).unwrap();
334 };
335 let (task, handle) = async_task::spawn(
336 future::poll_fn(|_| Poll::<()>::Pending),
337 schedule,
338 AtomicUsize::new(0),
339 );
340 task.schedule();
341
342 assert_eq!(handle.tag().load(Ordering::SeqCst), 1);
343 r.recv().unwrap().schedule();
344
345 assert_eq!(handle.tag().load(Ordering::SeqCst), 2);
346 r.recv().unwrap().schedule();
347
348 assert_eq!(handle.tag().load(Ordering::SeqCst), 3);
349 r.recv().unwrap();
350}
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100351
352#[test]
353fn drop_inside_schedule() {
354 struct DropGuard(AtomicUsize);
355 impl Drop for DropGuard {
356 fn drop(&mut self) {
357 self.0.fetch_add(1, Ordering::SeqCst);
358 }
359 }
360 let guard = DropGuard(AtomicUsize::new(0));
361
362 let (task, _) = async_task::spawn(
363 async {},
364 move |task| {
365 assert_eq!(guard.0.load(Ordering::SeqCst), 0);
366 drop(task);
367 assert_eq!(guard.0.load(Ordering::SeqCst), 0);
368 },
369 (),
370 );
371 task.schedule();
372}
Stjepan Glavinaaf051a52020-01-06 15:25:52 -0600373
374#[test]
375fn waker() {
376 let (s, r) = channel::unbounded();
377 let schedule = move |t| s.send(t).unwrap();
378 let (task, handle) = async_task::spawn(
379 future::poll_fn(|_| Poll::<()>::Pending),
380 schedule,
381 Box::new(0),
382 );
383
384 assert!(r.is_empty());
385 let w = task.waker();
386 task.run();
387 w.wake();
388
389 let task = r.recv().unwrap();
390 task.run();
391 handle.waker().wake();
392
393 r.recv().unwrap();
394}
395
396#[test]
397fn raw() {
398 let (task, _handle) = async_task::spawn(async {}, |_| panic!(), Box::new(AtomicUsize::new(7)));
399
400 let a = task.into_raw();
401 let task = unsafe {
402 (*a).fetch_add(1, Ordering::SeqCst);
403 Task::from_raw(a)
404 };
405
406 assert_eq!(task.tag().load(Ordering::SeqCst), 8);
407 task.run();
408}