blob: b9e181b1a0899cb01f22bca6ee7e86a73785dcb5 [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001#![feature(async_await)]
2
3use std::future::Future;
4use std::pin::Pin;
5use std::sync::atomic::{AtomicUsize, Ordering};
6use std::task::{Context, Poll};
7
8use async_task::Task;
9use crossbeam::atomic::AtomicCell;
10use crossbeam::channel;
11use futures::future;
12use lazy_static::lazy_static;
13
14// Creates a future with event counters.
15//
16// Usage: `future!(f, POLL, DROP)`
17//
18// The future `f` always returns `Poll::Ready`.
19// When it gets polled, `POLL` is incremented.
20// When it gets dropped, `DROP` is incremented.
21macro_rules! future {
22 ($name:pat, $poll:ident, $drop:ident) => {
23 lazy_static! {
24 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
25 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
26 }
27
28 let $name = {
29 struct Fut(Box<i32>);
30
31 impl Future for Fut {
32 type Output = Box<i32>;
33
34 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
35 $poll.fetch_add(1);
36 Poll::Ready(Box::new(0))
37 }
38 }
39
40 impl Drop for Fut {
41 fn drop(&mut self) {
42 $drop.fetch_add(1);
43 }
44 }
45
46 Fut(Box::new(0))
47 };
48 };
49}
50
51// Creates a schedule function with event counters.
52//
53// Usage: `schedule!(s, SCHED, DROP)`
54//
55// The schedule function `s` does nothing.
56// When it gets invoked, `SCHED` is incremented.
57// When it gets dropped, `DROP` is incremented.
58macro_rules! schedule {
59 ($name:pat, $sched:ident, $drop:ident) => {
60 lazy_static! {
61 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
62 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
63 }
64
65 let $name = {
66 struct Guard(Box<i32>);
67
68 impl Drop for Guard {
69 fn drop(&mut self) {
70 $drop.fetch_add(1);
71 }
72 }
73
74 let guard = Guard(Box::new(0));
75 move |_task| {
76 &guard;
77 $sched.fetch_add(1);
78 }
79 };
80 };
81}
82
83// Creates a task with event counters.
84//
85// Usage: `task!(task, handle f, s, DROP)`
86//
87// A task with future `f` and schedule function `s` is created.
88// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
89// When the tag inside the task gets dropped, `DROP` is incremented.
90macro_rules! task {
91 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
92 lazy_static! {
93 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
94 }
95
96 let ($task, $handle) = {
97 struct Tag(Box<i32>);
98
99 impl Drop for Tag {
100 fn drop(&mut self) {
101 $drop.fetch_add(1);
102 }
103 }
104
105 async_task::spawn($future, $schedule, Tag(Box::new(0)))
106 };
107 };
108}
109
110#[test]
111fn cancel_and_drop_handle() {
112 future!(f, POLL, DROP_F);
113 schedule!(s, SCHEDULE, DROP_S);
114 task!(task, handle, f, s, DROP_D);
115
116 assert_eq!(POLL.load(), 0);
117 assert_eq!(SCHEDULE.load(), 0);
118 assert_eq!(DROP_F.load(), 0);
119 assert_eq!(DROP_S.load(), 0);
120 assert_eq!(DROP_D.load(), 0);
121
122 task.cancel();
123 assert_eq!(POLL.load(), 0);
124 assert_eq!(SCHEDULE.load(), 0);
125 assert_eq!(DROP_F.load(), 0);
126 assert_eq!(DROP_S.load(), 0);
127 assert_eq!(DROP_D.load(), 0);
128
129 drop(handle);
130 assert_eq!(POLL.load(), 0);
131 assert_eq!(SCHEDULE.load(), 0);
132 assert_eq!(DROP_F.load(), 0);
133 assert_eq!(DROP_S.load(), 0);
134 assert_eq!(DROP_D.load(), 0);
135
136 drop(task);
137 assert_eq!(POLL.load(), 0);
138 assert_eq!(SCHEDULE.load(), 0);
139 assert_eq!(DROP_F.load(), 1);
140 assert_eq!(DROP_S.load(), 1);
141 assert_eq!(DROP_D.load(), 1);
142}
143
144#[test]
145fn run_and_drop_handle() {
146 future!(f, POLL, DROP_F);
147 schedule!(s, SCHEDULE, DROP_S);
148 task!(task, handle, f, s, DROP_D);
149
150 drop(handle);
151 assert_eq!(POLL.load(), 0);
152 assert_eq!(SCHEDULE.load(), 0);
153 assert_eq!(DROP_F.load(), 0);
154 assert_eq!(DROP_S.load(), 0);
155 assert_eq!(DROP_D.load(), 0);
156
157 task.run();
158 assert_eq!(POLL.load(), 1);
159 assert_eq!(SCHEDULE.load(), 0);
160 assert_eq!(DROP_F.load(), 1);
161 assert_eq!(DROP_S.load(), 1);
162 assert_eq!(DROP_D.load(), 1);
163}
164
165#[test]
166fn drop_handle_and_run() {
167 future!(f, POLL, DROP_F);
168 schedule!(s, SCHEDULE, DROP_S);
169 task!(task, handle, f, s, DROP_D);
170
171 drop(handle);
172 assert_eq!(POLL.load(), 0);
173 assert_eq!(SCHEDULE.load(), 0);
174 assert_eq!(DROP_F.load(), 0);
175 assert_eq!(DROP_S.load(), 0);
176 assert_eq!(DROP_D.load(), 0);
177
178 task.run();
179 assert_eq!(POLL.load(), 1);
180 assert_eq!(SCHEDULE.load(), 0);
181 assert_eq!(DROP_F.load(), 1);
182 assert_eq!(DROP_S.load(), 1);
183 assert_eq!(DROP_D.load(), 1);
184}
185
186#[test]
187fn cancel_and_run() {
188 future!(f, POLL, DROP_F);
189 schedule!(s, SCHEDULE, DROP_S);
190 task!(task, handle, f, s, DROP_D);
191
192 handle.cancel();
193 assert_eq!(POLL.load(), 0);
194 assert_eq!(SCHEDULE.load(), 0);
195 assert_eq!(DROP_F.load(), 0);
196 assert_eq!(DROP_S.load(), 0);
197 assert_eq!(DROP_D.load(), 0);
198
199 drop(handle);
200 assert_eq!(POLL.load(), 0);
201 assert_eq!(SCHEDULE.load(), 0);
202 assert_eq!(DROP_F.load(), 0);
203 assert_eq!(DROP_S.load(), 0);
204 assert_eq!(DROP_D.load(), 0);
205
206 task.run();
207 assert_eq!(POLL.load(), 0);
208 assert_eq!(SCHEDULE.load(), 0);
209 assert_eq!(DROP_F.load(), 1);
210 assert_eq!(DROP_S.load(), 1);
211 assert_eq!(DROP_D.load(), 1);
212}
213
214#[test]
215fn run_and_cancel() {
216 future!(f, POLL, DROP_F);
217 schedule!(s, SCHEDULE, DROP_S);
218 task!(task, handle, f, s, DROP_D);
219
220 task.run();
221 assert_eq!(POLL.load(), 1);
222 assert_eq!(SCHEDULE.load(), 0);
223 assert_eq!(DROP_F.load(), 1);
224 assert_eq!(DROP_S.load(), 0);
225 assert_eq!(DROP_D.load(), 0);
226
227 handle.cancel();
228 assert_eq!(POLL.load(), 1);
229 assert_eq!(SCHEDULE.load(), 0);
230 assert_eq!(DROP_F.load(), 1);
231 assert_eq!(DROP_S.load(), 0);
232 assert_eq!(DROP_D.load(), 0);
233
234 drop(handle);
235 assert_eq!(POLL.load(), 1);
236 assert_eq!(SCHEDULE.load(), 0);
237 assert_eq!(DROP_F.load(), 1);
238 assert_eq!(DROP_S.load(), 1);
239 assert_eq!(DROP_D.load(), 1);
240}
241
242#[test]
243fn schedule() {
244 let (s, r) = channel::unbounded();
245 let schedule = move |t| s.send(t).unwrap();
246 let (task, _handle) = async_task::spawn(
247 future::poll_fn(|_| Poll::<()>::Pending),
248 schedule,
249 Box::new(0),
250 );
251
252 assert!(r.is_empty());
253 task.schedule();
254
255 let task = r.recv().unwrap();
256 assert!(r.is_empty());
257 task.schedule();
258
259 let task = r.recv().unwrap();
260 assert!(r.is_empty());
261 task.schedule();
262
263 r.recv().unwrap();
264}
265
266#[test]
267fn tag() {
268 let (s, r) = channel::unbounded();
269 let schedule = move |t| s.send(t).unwrap();
270 let (task, handle) = async_task::spawn(
271 future::poll_fn(|_| Poll::<()>::Pending),
272 schedule,
273 AtomicUsize::new(7),
274 );
275
276 assert!(r.is_empty());
277 task.schedule();
278
279 let task = r.recv().unwrap();
280 assert!(r.is_empty());
281 handle.tag().fetch_add(1, Ordering::SeqCst);
282 task.schedule();
283
284 let task = r.recv().unwrap();
285 assert_eq!(task.tag().load(Ordering::SeqCst), 8);
286 assert!(r.is_empty());
287 task.schedule();
288
289 r.recv().unwrap();
290}
291
292#[test]
293fn schedule_counter() {
294 let (s, r) = channel::unbounded();
295 let schedule = move |t: Task<AtomicUsize>| {
296 t.tag().fetch_add(1, Ordering::SeqCst);
297 s.send(t).unwrap();
298 };
299 let (task, handle) = async_task::spawn(
300 future::poll_fn(|_| Poll::<()>::Pending),
301 schedule,
302 AtomicUsize::new(0),
303 );
304 task.schedule();
305
306 assert_eq!(handle.tag().load(Ordering::SeqCst), 1);
307 r.recv().unwrap().schedule();
308
309 assert_eq!(handle.tag().load(Ordering::SeqCst), 2);
310 r.recv().unwrap().schedule();
311
312 assert_eq!(handle.tag().load(Ordering::SeqCst), 3);
313 r.recv().unwrap();
314}