blob: e08293948d2eae38f8a96fc3f992b7ad74752ba8 [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001#![feature(async_await)]
2
3use std::cell::Cell;
4use std::future::Future;
5use std::pin::Pin;
6use std::task::{Context, Poll};
7use std::thread;
8use std::time::Duration;
9
10use async_task::Task;
11use crossbeam::atomic::AtomicCell;
12use futures::executor::block_on;
13use futures::future;
14use lazy_static::lazy_static;
15
16// Creates a future with event counters.
17//
18// Usage: `future!(f, POLL, DROP_F, DROP_O)`
19//
20// The future `f` outputs `Poll::Ready`.
21// When it gets polled, `POLL` is incremented.
22// When it gets dropped, `DROP_F` is incremented.
23// When the output gets dropped, `DROP_O` is incremented.
24macro_rules! future {
25 ($name:pat, $poll:ident, $drop_f:ident, $drop_o:ident) => {
26 lazy_static! {
27 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
28 static ref $drop_f: AtomicCell<usize> = AtomicCell::new(0);
29 static ref $drop_o: AtomicCell<usize> = AtomicCell::new(0);
30 }
31
32 let $name = {
33 struct Fut(Box<i32>);
34
35 impl Future for Fut {
36 type Output = Out;
37
38 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
39 $poll.fetch_add(1);
40 Poll::Ready(Out(Box::new(0)))
41 }
42 }
43
44 impl Drop for Fut {
45 fn drop(&mut self) {
46 $drop_f.fetch_add(1);
47 }
48 }
49
50 struct Out(Box<i32>);
51
52 impl Drop for Out {
53 fn drop(&mut self) {
54 $drop_o.fetch_add(1);
55 }
56 }
57
58 Fut(Box::new(0))
59 };
60 };
61}
62
63// Creates a schedule function with event counters.
64//
65// Usage: `schedule!(s, SCHED, DROP)`
66//
67// The schedule function `s` does nothing.
68// When it gets invoked, `SCHED` is incremented.
69// When it gets dropped, `DROP` is incremented.
70macro_rules! schedule {
71 ($name:pat, $sched:ident, $drop:ident) => {
72 lazy_static! {
73 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
74 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
75 }
76
77 let $name = {
78 struct Guard(Box<i32>);
79
80 impl Drop for Guard {
81 fn drop(&mut self) {
82 $drop.fetch_add(1);
83 }
84 }
85
86 let guard = Guard(Box::new(0));
87 move |task: Task<_>| {
88 &guard;
89 task.schedule();
90 $sched.fetch_add(1);
91 }
92 };
93 };
94}
95
96// Creates a task with event counters.
97//
98// Usage: `task!(task, handle f, s, DROP)`
99//
100// A task with future `f` and schedule function `s` is created.
101// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
102// When the tag inside the task gets dropped, `DROP` is incremented.
103macro_rules! task {
104 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
105 lazy_static! {
106 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
107 }
108
109 let ($task, $handle) = {
110 struct Tag(Box<i32>);
111
112 impl Drop for Tag {
113 fn drop(&mut self) {
114 $drop.fetch_add(1);
115 }
116 }
117
118 async_task::spawn($future, $schedule, Tag(Box::new(0)))
119 };
120 };
121}
122
123fn ms(ms: u64) -> Duration {
124 Duration::from_millis(ms)
125}
126
127#[test]
128fn cancel_and_join() {
129 future!(f, POLL, DROP_F, DROP_O);
130 schedule!(s, SCHEDULE, DROP_S);
131 task!(task, handle, f, s, DROP_D);
132
133 assert_eq!(DROP_O.load(), 0);
134
135 task.cancel();
136 drop(task);
137 assert_eq!(DROP_O.load(), 0);
138
139 assert!(block_on(handle).is_none());
140 assert_eq!(POLL.load(), 0);
141 assert_eq!(SCHEDULE.load(), 0);
142 assert_eq!(DROP_F.load(), 1);
143 assert_eq!(DROP_S.load(), 1);
144 assert_eq!(DROP_D.load(), 1);
145 assert_eq!(DROP_O.load(), 0);
146}
147
148#[test]
149fn run_and_join() {
150 future!(f, POLL, DROP_F, DROP_O);
151 schedule!(s, SCHEDULE, DROP_S);
152 task!(task, handle, f, s, DROP_D);
153
154 assert_eq!(DROP_O.load(), 0);
155
156 task.run();
157 assert_eq!(DROP_O.load(), 0);
158
159 assert!(block_on(handle).is_some());
160 assert_eq!(POLL.load(), 1);
161 assert_eq!(SCHEDULE.load(), 0);
162 assert_eq!(DROP_F.load(), 1);
163 assert_eq!(DROP_S.load(), 1);
164 assert_eq!(DROP_D.load(), 1);
165 assert_eq!(DROP_O.load(), 1);
166}
167
168#[test]
169fn drop_handle_and_run() {
170 future!(f, POLL, DROP_F, DROP_O);
171 schedule!(s, SCHEDULE, DROP_S);
172 task!(task, handle, f, s, DROP_D);
173
174 assert_eq!(DROP_O.load(), 0);
175
176 drop(handle);
177 assert_eq!(DROP_O.load(), 0);
178
179 task.run();
180 assert_eq!(POLL.load(), 1);
181 assert_eq!(SCHEDULE.load(), 0);
182 assert_eq!(DROP_F.load(), 1);
183 assert_eq!(DROP_S.load(), 1);
184 assert_eq!(DROP_D.load(), 1);
185 assert_eq!(DROP_O.load(), 1);
186}
187
188#[test]
189fn join_twice() {
190 future!(f, POLL, DROP_F, DROP_O);
191 schedule!(s, SCHEDULE, DROP_S);
192 task!(task, mut handle, f, s, DROP_D);
193
194 assert_eq!(DROP_O.load(), 0);
195
196 task.run();
197 assert_eq!(DROP_O.load(), 0);
198
199 assert!(block_on(&mut handle).is_some());
200 assert_eq!(POLL.load(), 1);
201 assert_eq!(SCHEDULE.load(), 0);
202 assert_eq!(DROP_F.load(), 1);
203 assert_eq!(DROP_S.load(), 0);
204 assert_eq!(DROP_D.load(), 0);
205 assert_eq!(DROP_O.load(), 1);
206
207 assert!(block_on(&mut handle).is_none());
208 assert_eq!(POLL.load(), 1);
209 assert_eq!(SCHEDULE.load(), 0);
210 assert_eq!(DROP_F.load(), 1);
211 assert_eq!(DROP_S.load(), 0);
212 assert_eq!(DROP_D.load(), 0);
213 assert_eq!(DROP_O.load(), 1);
214
215 drop(handle);
216 assert_eq!(DROP_S.load(), 1);
217 assert_eq!(DROP_D.load(), 1);
218}
219
220#[test]
221fn join_and_cancel() {
222 future!(f, POLL, DROP_F, DROP_O);
223 schedule!(s, SCHEDULE, DROP_S);
224 task!(task, handle, f, s, DROP_D);
225
226 crossbeam::scope(|scope| {
227 scope.spawn(|_| {
228 thread::sleep(ms(100));
229
230 task.cancel();
231 drop(task);
232
233 thread::sleep(ms(200));
234 assert_eq!(POLL.load(), 0);
235 assert_eq!(SCHEDULE.load(), 0);
236 assert_eq!(DROP_F.load(), 1);
237 assert_eq!(DROP_O.load(), 0);
238 assert_eq!(DROP_S.load(), 1);
239 assert_eq!(DROP_D.load(), 1);
240 });
241
242 assert!(block_on(handle).is_none());
243 assert_eq!(POLL.load(), 0);
244 assert_eq!(SCHEDULE.load(), 0);
245
246 thread::sleep(ms(100));
247 assert_eq!(DROP_F.load(), 1);
248 assert_eq!(DROP_O.load(), 0);
249 assert_eq!(DROP_S.load(), 1);
250 assert_eq!(DROP_D.load(), 1);
251 })
252 .unwrap();
253}
254
255#[test]
256fn join_and_run() {
257 future!(f, POLL, DROP_F, DROP_O);
258 schedule!(s, SCHEDULE, DROP_S);
259 task!(task, handle, f, s, DROP_D);
260
261 crossbeam::scope(|scope| {
262 scope.spawn(|_| {
263 thread::sleep(ms(200));
264
265 task.run();
266 assert_eq!(POLL.load(), 1);
267 assert_eq!(SCHEDULE.load(), 0);
268 assert_eq!(DROP_F.load(), 1);
269
270 thread::sleep(ms(100));
271 assert_eq!(DROP_S.load(), 1);
272 assert_eq!(DROP_D.load(), 1);
273 });
274
275 assert!(block_on(handle).is_some());
276 assert_eq!(POLL.load(), 1);
277 assert_eq!(SCHEDULE.load(), 0);
278 assert_eq!(DROP_F.load(), 1);
279 assert_eq!(DROP_O.load(), 1);
280
281 thread::sleep(ms(100));
282 assert_eq!(DROP_S.load(), 1);
283 assert_eq!(DROP_D.load(), 1);
284 })
285 .unwrap();
286}
287
288#[test]
289fn try_join_and_run_and_join() {
290 future!(f, POLL, DROP_F, DROP_O);
291 schedule!(s, SCHEDULE, DROP_S);
292 task!(task, mut handle, f, s, DROP_D);
293
294 crossbeam::scope(|scope| {
295 scope.spawn(|_| {
296 thread::sleep(ms(200));
297
298 task.run();
299 assert_eq!(POLL.load(), 1);
300 assert_eq!(SCHEDULE.load(), 0);
301 assert_eq!(DROP_F.load(), 1);
302
303 thread::sleep(ms(100));
304 assert_eq!(DROP_S.load(), 1);
305 assert_eq!(DROP_D.load(), 1);
306 });
307
308 block_on(future::select(&mut handle, future::ready(())));
309 assert_eq!(POLL.load(), 0);
310 assert_eq!(SCHEDULE.load(), 0);
311 assert_eq!(DROP_F.load(), 0);
312 assert_eq!(DROP_S.load(), 0);
313 assert_eq!(DROP_D.load(), 0);
314 assert_eq!(DROP_O.load(), 0);
315
316 assert!(block_on(handle).is_some());
317 assert_eq!(POLL.load(), 1);
318 assert_eq!(SCHEDULE.load(), 0);
319 assert_eq!(DROP_F.load(), 1);
320 assert_eq!(DROP_O.load(), 1);
321
322 thread::sleep(ms(100));
323 assert_eq!(DROP_S.load(), 1);
324 assert_eq!(DROP_D.load(), 1);
325 })
326 .unwrap();
327}
328
329#[test]
330fn try_join_and_cancel_and_run() {
331 future!(f, POLL, DROP_F, DROP_O);
332 schedule!(s, SCHEDULE, DROP_S);
333 task!(task, mut handle, f, s, DROP_D);
334
335 crossbeam::scope(|scope| {
336 scope.spawn(|_| {
337 thread::sleep(ms(200));
338
339 task.run();
340 assert_eq!(POLL.load(), 0);
341 assert_eq!(SCHEDULE.load(), 0);
342 assert_eq!(DROP_F.load(), 1);
343 assert_eq!(DROP_S.load(), 1);
344 assert_eq!(DROP_D.load(), 1);
345 });
346
347 block_on(future::select(&mut handle, future::ready(())));
348 assert_eq!(POLL.load(), 0);
349 assert_eq!(SCHEDULE.load(), 0);
350 assert_eq!(DROP_F.load(), 0);
351 assert_eq!(DROP_S.load(), 0);
352 assert_eq!(DROP_D.load(), 0);
353 assert_eq!(DROP_O.load(), 0);
354
355 handle.cancel();
356 assert_eq!(POLL.load(), 0);
357 assert_eq!(SCHEDULE.load(), 0);
358 assert_eq!(DROP_F.load(), 0);
359 assert_eq!(DROP_S.load(), 0);
360 assert_eq!(DROP_D.load(), 0);
361 assert_eq!(DROP_O.load(), 0);
362
363 drop(handle);
364 assert_eq!(POLL.load(), 0);
365 assert_eq!(SCHEDULE.load(), 0);
366 assert_eq!(DROP_F.load(), 0);
367 assert_eq!(DROP_S.load(), 0);
368 assert_eq!(DROP_D.load(), 0);
369 assert_eq!(DROP_O.load(), 0);
370 })
371 .unwrap();
372}
373
374#[test]
375fn try_join_and_run_and_cancel() {
376 future!(f, POLL, DROP_F, DROP_O);
377 schedule!(s, SCHEDULE, DROP_S);
378 task!(task, mut handle, f, s, DROP_D);
379
380 crossbeam::scope(|scope| {
381 scope.spawn(|_| {
382 thread::sleep(ms(200));
383
384 task.run();
385 assert_eq!(POLL.load(), 1);
386 assert_eq!(SCHEDULE.load(), 0);
387 assert_eq!(DROP_F.load(), 1);
388 assert_eq!(DROP_S.load(), 0);
389 assert_eq!(DROP_D.load(), 0);
390 });
391
392 block_on(future::select(&mut handle, future::ready(())));
393 assert_eq!(POLL.load(), 0);
394 assert_eq!(SCHEDULE.load(), 0);
395 assert_eq!(DROP_F.load(), 0);
396 assert_eq!(DROP_S.load(), 0);
397 assert_eq!(DROP_D.load(), 0);
398 assert_eq!(DROP_O.load(), 0);
399
400 thread::sleep(ms(400));
401
402 handle.cancel();
403 assert_eq!(POLL.load(), 1);
404 assert_eq!(SCHEDULE.load(), 0);
405 assert_eq!(DROP_F.load(), 1);
406 assert_eq!(DROP_S.load(), 0);
407 assert_eq!(DROP_D.load(), 0);
408 assert_eq!(DROP_O.load(), 0);
409
410 drop(handle);
411 assert_eq!(POLL.load(), 1);
412 assert_eq!(SCHEDULE.load(), 0);
413 assert_eq!(DROP_F.load(), 1);
414 assert_eq!(DROP_S.load(), 1);
415 assert_eq!(DROP_D.load(), 1);
416 assert_eq!(DROP_O.load(), 1);
417 })
418 .unwrap();
419}
420
421#[test]
422fn await_output() {
423 struct Fut<T>(Cell<Option<T>>);
424
425 impl<T> Fut<T> {
426 fn new(t: T) -> Fut<T> {
427 Fut(Cell::new(Some(t)))
428 }
429 }
430
431 impl<T> Future for Fut<T> {
432 type Output = T;
433
434 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
435 Poll::Ready(self.0.take().unwrap())
436 }
437 }
438
439 for i in 0..10 {
440 let (task, handle) = async_task::spawn(Fut::new(i), drop, Box::new(0));
441 task.run();
442 assert_eq!(block_on(handle), Some(i));
443 }
444
445 for i in 0..10 {
446 let (task, handle) = async_task::spawn(Fut::new(vec![7; i]), drop, Box::new(0));
447 task.run();
448 assert_eq!(block_on(handle), Some(vec![7; i]));
449 }
450
451 let (task, handle) = async_task::spawn(Fut::new("foo".to_string()), drop, Box::new(0));
452 task.run();
453 assert_eq!(block_on(handle), Some("foo".to_string()));
454}