blob: 7b7d1b555d5a9a1a3f83d614711d473304592943 [file] [log] [blame]
Stjepan Glavina1479e862019-08-12 20:18:51 +02001use std::future::Future;
2use std::panic::catch_unwind;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5use std::thread;
6use std::time::Duration;
7
8use async_task::Task;
9use crossbeam::atomic::AtomicCell;
10use futures::executor::block_on;
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` sleeps for 200 ms and then panics.
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 = ();
33
34 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
35 $poll.fetch_add(1);
36 thread::sleep(ms(200));
37 panic!()
38 }
39 }
40
41 impl Drop for Fut {
42 fn drop(&mut self) {
43 $drop.fetch_add(1);
44 }
45 }
46
47 Fut(Box::new(0))
48 };
49 };
50}
51
52// Creates a schedule function with event counters.
53//
54// Usage: `schedule!(s, SCHED, DROP)`
55//
56// The schedule function `s` does nothing.
57// When it gets invoked, `SCHED` is incremented.
58// When it gets dropped, `DROP` is incremented.
59macro_rules! schedule {
60 ($name:pat, $sched:ident, $drop:ident) => {
61 lazy_static! {
62 static ref $sched: AtomicCell<usize> = AtomicCell::new(0);
63 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
64 }
65
66 let $name = {
67 struct Guard(Box<i32>);
68
69 impl Drop for Guard {
70 fn drop(&mut self) {
71 $drop.fetch_add(1);
72 }
73 }
74
75 let guard = Guard(Box::new(0));
76 move |_task: Task<_>| {
77 &guard;
78 $sched.fetch_add(1);
79 }
80 };
81 };
82}
83
84// Creates a task with event counters.
85//
86// Usage: `task!(task, handle f, s, DROP)`
87//
88// A task with future `f` and schedule function `s` is created.
89// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
90// When the tag inside the task gets dropped, `DROP` is incremented.
91macro_rules! task {
92 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
93 lazy_static! {
94 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
95 }
96
97 let ($task, $handle) = {
98 struct Tag(Box<i32>);
99
100 impl Drop for Tag {
101 fn drop(&mut self) {
102 $drop.fetch_add(1);
103 }
104 }
105
106 async_task::spawn($future, $schedule, Tag(Box::new(0)))
107 };
108 };
109}
110
111fn ms(ms: u64) -> Duration {
112 Duration::from_millis(ms)
113}
114
115#[test]
116fn cancel_during_run() {
117 future!(f, POLL, DROP_F);
118 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100119 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200120
121 crossbeam::scope(|scope| {
122 scope.spawn(|_| {
123 assert!(catch_unwind(|| task.run()).is_err());
124 assert_eq!(POLL.load(), 1);
125 assert_eq!(SCHEDULE.load(), 0);
126 assert_eq!(DROP_F.load(), 1);
127 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100128 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200129 });
130
131 thread::sleep(ms(100));
132
133 handle.cancel();
134 assert_eq!(POLL.load(), 1);
135 assert_eq!(SCHEDULE.load(), 0);
136 assert_eq!(DROP_F.load(), 0);
137 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100138 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200139
140 drop(handle);
141 assert_eq!(POLL.load(), 1);
142 assert_eq!(SCHEDULE.load(), 0);
143 assert_eq!(DROP_F.load(), 0);
144 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100145 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200146 })
147 .unwrap();
148}
149
150#[test]
151fn run_and_join() {
152 future!(f, POLL, DROP_F);
153 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100154 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200155
156 assert!(catch_unwind(|| task.run()).is_err());
157 assert_eq!(POLL.load(), 1);
158 assert_eq!(SCHEDULE.load(), 0);
159 assert_eq!(DROP_F.load(), 1);
160 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100161 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200162
163 assert!(block_on(handle).is_none());
164 assert_eq!(POLL.load(), 1);
165 assert_eq!(SCHEDULE.load(), 0);
166 assert_eq!(DROP_F.load(), 1);
167 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100168 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200169}
170
171#[test]
172fn try_join_and_run_and_join() {
173 future!(f, POLL, DROP_F);
174 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100175 task!(task, mut handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200176
177 block_on(future::select(&mut handle, future::ready(())));
178 assert_eq!(POLL.load(), 0);
179 assert_eq!(SCHEDULE.load(), 0);
180 assert_eq!(DROP_F.load(), 0);
181 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100182 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200183
184 assert!(catch_unwind(|| task.run()).is_err());
185 assert_eq!(POLL.load(), 1);
186 assert_eq!(SCHEDULE.load(), 0);
187 assert_eq!(DROP_F.load(), 1);
188 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100189 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200190
191 assert!(block_on(handle).is_none());
192 assert_eq!(POLL.load(), 1);
193 assert_eq!(SCHEDULE.load(), 0);
194 assert_eq!(DROP_F.load(), 1);
195 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100196 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200197}
198
199#[test]
200fn join_during_run() {
201 future!(f, POLL, DROP_F);
202 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100203 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200204
205 crossbeam::scope(|scope| {
206 scope.spawn(|_| {
207 assert!(catch_unwind(|| task.run()).is_err());
208 assert_eq!(POLL.load(), 1);
209 assert_eq!(SCHEDULE.load(), 0);
210 assert_eq!(DROP_F.load(), 1);
211
212 thread::sleep(ms(100));
213 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100214 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200215 });
216
217 thread::sleep(ms(100));
218
219 assert!(block_on(handle).is_none());
220 assert_eq!(POLL.load(), 1);
221 assert_eq!(SCHEDULE.load(), 0);
222 assert_eq!(DROP_F.load(), 1);
223
224 thread::sleep(ms(100));
225 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100226 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200227 })
228 .unwrap();
229}
230
231#[test]
232fn try_join_during_run() {
233 future!(f, POLL, DROP_F);
234 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100235 task!(task, mut handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200236
237 crossbeam::scope(|scope| {
238 scope.spawn(|_| {
239 assert!(catch_unwind(|| task.run()).is_err());
240 assert_eq!(POLL.load(), 1);
241 assert_eq!(SCHEDULE.load(), 0);
242 assert_eq!(DROP_F.load(), 1);
243 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100244 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200245 });
246
247 thread::sleep(ms(100));
248
249 block_on(future::select(&mut handle, future::ready(())));
250 assert_eq!(POLL.load(), 1);
251 assert_eq!(SCHEDULE.load(), 0);
252 assert_eq!(DROP_F.load(), 0);
253 assert_eq!(DROP_S.load(), 0);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100254 assert_eq!(DROP_T.load(), 0);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200255 drop(handle);
256 })
257 .unwrap();
258}
259
260#[test]
261fn drop_handle_during_run() {
262 future!(f, POLL, DROP_F);
263 schedule!(s, SCHEDULE, DROP_S);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100264 task!(task, handle, f, s, DROP_T);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200265
266 crossbeam::scope(|scope| {
267 scope.spawn(|_| {
268 assert!(catch_unwind(|| task.run()).is_err());
269 assert_eq!(POLL.load(), 1);
270 assert_eq!(SCHEDULE.load(), 0);
271 assert_eq!(DROP_F.load(), 1);
272 assert_eq!(DROP_S.load(), 1);
Stjepan Glavina45a2c1f2019-12-31 01:38:58 +0100273 assert_eq!(DROP_T.load(), 1);
Stjepan Glavina1479e862019-08-12 20:18:51 +0200274 });
275
276 thread::sleep(ms(100));
277
278 drop(handle);
279 assert_eq!(POLL.load(), 1);
280 assert_eq!(SCHEDULE.load(), 0);
281 assert_eq!(DROP_F.load(), 0);
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 })
285 .unwrap();
286}