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