blob: 7d1d59ea94a0c5221e739ec811482c0aec19cdb7 [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001use std::cell::Cell;
2use std::future::Future;
3use std::pin::Pin;
4use std::task::Waker;
5use std::task::{Context, Poll};
6use std::thread;
7use std::time::Duration;
8
9use async_task::Task;
10use crossbeam::atomic::AtomicCell;
11use crossbeam::channel;
12use lazy_static::lazy_static;
13
14// Creates a future with event counters.
15//
16// Usage: `future!(f, waker, POLL, DROP)`
17//
18// The future `f` always sleeps for 200 ms, and returns `Poll::Ready` the second time it is polled.
19// When it gets polled, `POLL` is incremented.
20// When it gets dropped, `DROP` is incremented.
21//
22// Every time the future is run, it stores the waker into a global variable.
23// This waker can be extracted using the `waker` function.
24macro_rules! future {
25 ($name:pat, $waker:pat, $poll:ident, $drop:ident) => {
26 lazy_static! {
27 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
28 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
29 static ref WAKER: AtomicCell<Option<Waker>> = AtomicCell::new(None);
30 }
31
32 let ($name, $waker) = {
33 struct Fut(Cell<bool>, Box<i32>);
34
35 impl Future for Fut {
36 type Output = Box<i32>;
37
38 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
39 WAKER.store(Some(cx.waker().clone()));
40 $poll.fetch_add(1);
41 thread::sleep(ms(200));
42
43 if self.0.get() {
44 Poll::Ready(Box::new(0))
45 } else {
46 self.0.set(true);
47 Poll::Pending
48 }
49 }
50 }
51
52 impl Drop for Fut {
53 fn drop(&mut self) {
54 $drop.fetch_add(1);
55 }
56 }
57
58 (Fut(Cell::new(false), Box::new(0)), || {
59 WAKER.swap(None).unwrap()
60 })
61 };
62 };
63}
64
65// Creates a schedule function with event counters.
66//
67// Usage: `schedule!(s, chan, SCHED, DROP)`
68//
69// The schedule function `s` pushes the task into `chan`.
70// When it gets invoked, `SCHED` is incremented.
71// When it gets dropped, `DROP` is incremented.
72//
73// Receiver `chan` extracts the task when it is scheduled.
74macro_rules! schedule {
75 ($name:pat, $chan:pat, $sched:ident, $drop:ident) => {
76 lazy_static! {
77 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
78 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
79 }
80
81 let ($name, $chan) = {
82 let (s, r) = channel::unbounded();
83
84 struct Guard(Box<i32>);
85
86 impl Drop for Guard {
87 fn drop(&mut self) {
88 $drop.fetch_add(1);
89 }
90 }
91
92 let guard = Guard(Box::new(0));
93 let sched = move |task: Task<_>| {
94 &guard;
95 $sched.fetch_add(1);
96 s.send(task).unwrap();
97 };
98
99 (sched, r)
100 };
101 };
102}
103
104// Creates a task with event counters.
105//
106// Usage: `task!(task, handle f, s, DROP)`
107//
108// A task with future `f` and schedule function `s` is created.
109// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
110// When the tag inside the task gets dropped, `DROP` is incremented.
111macro_rules! task {
112 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
113 lazy_static! {
114 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
115 }
116
117 let ($task, $handle) = {
118 struct Tag(Box<i32>);
119
120 impl Drop for Tag {
121 fn drop(&mut self) {
122 $drop.fetch_add(1);
123 }
124 }
125
126 async_task::spawn($future, $schedule, Tag(Box::new(0)))
127 };
128 };
129}
130
131fn ms(ms: u64) -> Duration {
132 Duration::from_millis(ms)
133}
134
135#[test]
136fn wake() {
137 future!(f, waker, POLL, DROP_F);
138 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100139 task!(mut task, _, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200140
141 assert!(chan.is_empty());
142
143 task.run();
144 assert_eq!(POLL.load(), 1);
145 assert_eq!(SCHEDULE.load(), 0);
146 assert_eq!(DROP_F.load(), 0);
147 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100148 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200149 assert_eq!(chan.len(), 0);
150
151 waker().wake();
152 task = chan.recv().unwrap();
153 assert_eq!(POLL.load(), 1);
154 assert_eq!(SCHEDULE.load(), 1);
155 assert_eq!(DROP_F.load(), 0);
156 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100157 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200158 assert_eq!(chan.len(), 0);
159
160 task.run();
161 assert_eq!(POLL.load(), 2);
162 assert_eq!(SCHEDULE.load(), 1);
163 assert_eq!(DROP_F.load(), 1);
164 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100165 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200166 assert_eq!(chan.len(), 0);
167
168 waker().wake();
169 assert_eq!(POLL.load(), 2);
170 assert_eq!(SCHEDULE.load(), 1);
171 assert_eq!(DROP_F.load(), 1);
172 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100173 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200174 assert_eq!(chan.len(), 0);
175}
176
177#[test]
178fn wake_by_ref() {
179 future!(f, waker, POLL, DROP_F);
180 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100181 task!(mut task, _, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200182
183 assert!(chan.is_empty());
184
185 task.run();
186 assert_eq!(POLL.load(), 1);
187 assert_eq!(SCHEDULE.load(), 0);
188 assert_eq!(DROP_F.load(), 0);
189 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100190 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200191 assert_eq!(chan.len(), 0);
192
193 waker().wake_by_ref();
194 task = chan.recv().unwrap();
195 assert_eq!(POLL.load(), 1);
196 assert_eq!(SCHEDULE.load(), 1);
197 assert_eq!(DROP_F.load(), 0);
198 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100199 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200200 assert_eq!(chan.len(), 0);
201
202 task.run();
203 assert_eq!(POLL.load(), 2);
204 assert_eq!(SCHEDULE.load(), 1);
205 assert_eq!(DROP_F.load(), 1);
206 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100207 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200208 assert_eq!(chan.len(), 0);
209
210 waker().wake_by_ref();
211 assert_eq!(POLL.load(), 2);
212 assert_eq!(SCHEDULE.load(), 1);
213 assert_eq!(DROP_F.load(), 1);
214 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100215 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200216 assert_eq!(chan.len(), 0);
217}
218
219#[test]
220fn clone() {
221 future!(f, waker, POLL, DROP_F);
222 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100223 task!(mut task, _, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200224
225 task.run();
226 assert_eq!(POLL.load(), 1);
227 assert_eq!(SCHEDULE.load(), 0);
228 assert_eq!(DROP_F.load(), 0);
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 assert_eq!(chan.len(), 0);
232
233 let w2 = waker().clone();
234 let w3 = w2.clone();
235 let w4 = w3.clone();
236 w4.wake();
237
238 task = chan.recv().unwrap();
239 task.run();
240 assert_eq!(POLL.load(), 2);
241 assert_eq!(SCHEDULE.load(), 1);
242 assert_eq!(DROP_F.load(), 1);
243 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100244 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200245 assert_eq!(chan.len(), 0);
246
247 w3.wake();
248 assert_eq!(POLL.load(), 2);
249 assert_eq!(SCHEDULE.load(), 1);
250 assert_eq!(DROP_F.load(), 1);
251 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100252 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200253 assert_eq!(chan.len(), 0);
254
255 drop(w2);
256 drop(waker());
257 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100258 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200259}
260
261#[test]
Stjepan Glavinad7b17fb2020-04-14 14:38:57 +0200262fn wake_canceled() {
Stjepan Glavina1479e862019-08-12 20:18:51 +0200263 future!(f, waker, POLL, DROP_F);
264 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100265 task!(task, _, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200266
267 task.run();
268 assert_eq!(POLL.load(), 1);
269 assert_eq!(SCHEDULE.load(), 0);
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 let w = waker();
276
277 w.wake_by_ref();
278 chan.recv().unwrap().cancel();
279 assert_eq!(POLL.load(), 1);
280 assert_eq!(SCHEDULE.load(), 1);
281 assert_eq!(DROP_F.load(), 1);
282 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100283 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200284 assert_eq!(chan.len(), 0);
285
286 w.wake();
287 assert_eq!(POLL.load(), 1);
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
295#[test]
296fn wake_completed() {
297 future!(f, waker, POLL, DROP_F);
298 schedule!(s, chan, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100299 task!(task, _, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200300
301 task.run();
302 let w = waker();
303 assert_eq!(POLL.load(), 1);
304 assert_eq!(SCHEDULE.load(), 0);
305 assert_eq!(DROP_F.load(), 0);
306 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100307 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200308 assert_eq!(chan.len(), 0);
309
310 w.wake();
311 chan.recv().unwrap().run();
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(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100316 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200317 assert_eq!(chan.len(), 0);
318
319 waker().wake();
320 assert_eq!(POLL.load(), 2);
321 assert_eq!(SCHEDULE.load(), 1);
322 assert_eq!(DROP_F.load(), 1);
323 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100324 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200325 assert_eq!(chan.len(), 0);
326}