blob: 8426d2a19e0ca632a1442ce6f0c9e093afc56b9e [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;
9use futures::future;
10use 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]
241fn schedule() {
242 let (s, r) = channel::unbounded();
243 let schedule = move |t| s.send(t).unwrap();
244 let (task, _handle) = async_task::spawn(
245 future::poll_fn(|_| Poll::<()>::Pending),
246 schedule,
247 Box::new(0),
248 );
249
250 assert!(r.is_empty());
251 task.schedule();
252
253 let task = r.recv().unwrap();
254 assert!(r.is_empty());
255 task.schedule();
256
257 let task = r.recv().unwrap();
258 assert!(r.is_empty());
259 task.schedule();
260
261 r.recv().unwrap();
262}
263
264#[test]
265fn tag() {
266 let (s, r) = channel::unbounded();
267 let schedule = move |t| s.send(t).unwrap();
268 let (task, handle) = async_task::spawn(
269 future::poll_fn(|_| Poll::<()>::Pending),
270 schedule,
271 AtomicUsize::new(7),
272 );
273
274 assert!(r.is_empty());
275 task.schedule();
276
277 let task = r.recv().unwrap();
278 assert!(r.is_empty());
279 handle.tag().fetch_add(1, Ordering::SeqCst);
280 task.schedule();
281
282 let task = r.recv().unwrap();
283 assert_eq!(task.tag().load(Ordering::SeqCst), 8);
284 assert!(r.is_empty());
285 task.schedule();
286
287 r.recv().unwrap();
288}
289
290#[test]
291fn schedule_counter() {
292 let (s, r) = channel::unbounded();
293 let schedule = move |t: Task<AtomicUsize>| {
294 t.tag().fetch_add(1, Ordering::SeqCst);
295 s.send(t).unwrap();
296 };
297 let (task, handle) = async_task::spawn(
298 future::poll_fn(|_| Poll::<()>::Pending),
299 schedule,
300 AtomicUsize::new(0),
301 );
302 task.schedule();
303
304 assert_eq!(handle.tag().load(Ordering::SeqCst), 1);
305 r.recv().unwrap().schedule();
306
307 assert_eq!(handle.tag().load(Ordering::SeqCst), 2);
308 r.recv().unwrap().schedule();
309
310 assert_eq!(handle.tag().load(Ordering::SeqCst), 3);
311 r.recv().unwrap();
312}
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100313
314#[test]
315fn drop_inside_schedule() {
316 struct DropGuard(AtomicUsize);
317 impl Drop for DropGuard {
318 fn drop(&mut self) {
319 self.0.fetch_add(1, Ordering::SeqCst);
320 }
321 }
322 let guard = DropGuard(AtomicUsize::new(0));
323
324 let (task, _) = async_task::spawn(
325 async {},
326 move |task| {
327 assert_eq!(guard.0.load(Ordering::SeqCst), 0);
328 drop(task);
329 assert_eq!(guard.0.load(Ordering::SeqCst), 0);
330 },
331 (),
332 );
333 task.schedule();
334}