blob: ecca328b248461f97356044e12584987e43840af [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::task::{Context, Poll};
6use std::thread;
7use std::time::Duration;
8
9use async_task::Task;
10use crossbeam::atomic::AtomicCell;
11use futures::executor::block_on;
12use futures::future;
13use lazy_static::lazy_static;
14
15// Creates a future with event counters.
16//
17// Usage: `future!(f, POLL, DROP_F, DROP_O)`
18//
19// The future `f` sleeps for 200 ms and outputs `Poll::Ready`.
20// When it gets polled, `POLL` is incremented.
21// When it gets dropped, `DROP_F` is incremented.
22// When the output gets dropped, `DROP_O` is incremented.
23macro_rules! future {
24 ($name:pat, $poll:ident, $drop_f:ident, $drop_o:ident) => {
25 lazy_static! {
26 static ref $poll: AtomicCell<usize> = AtomicCell::new(0);
27 static ref $drop_f: AtomicCell<usize> = AtomicCell::new(0);
28 static ref $drop_o: AtomicCell<usize> = AtomicCell::new(0);
29 }
30
31 let $name = {
32 struct Fut(Box<i32>);
33
34 impl Future for Fut {
35 type Output = Out;
36
37 fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
38 $poll.fetch_add(1);
39 thread::sleep(ms(200));
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 $sched.fetch_add(1);
90 }
91 };
92 };
93}
94
95// Creates a task with event counters.
96//
97// Usage: `task!(task, handle f, s, DROP)`
98//
99// A task with future `f` and schedule function `s` is created.
100// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
101// When the tag inside the task gets dropped, `DROP` is incremented.
102macro_rules! task {
103 ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
104 lazy_static! {
105 static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
106 }
107
108 let ($task, $handle) = {
109 struct Tag(Box<i32>);
110
111 impl Drop for Tag {
112 fn drop(&mut self) {
113 $drop.fetch_add(1);
114 }
115 }
116
117 async_task::spawn($future, $schedule, Tag(Box::new(0)))
118 };
119 };
120}
121
122fn ms(ms: u64) -> Duration {
123 Duration::from_millis(ms)
124}
125
126#[test]
127fn cancel_during_run() {
128 future!(f, POLL, DROP_F, DROP_O);
129 schedule!(s, SCHEDULE, DROP_S);
130 task!(task, handle, f, s, DROP_D);
131
132 crossbeam::scope(|scope| {
133 scope.spawn(|_| {
134 task.run();
135 assert_eq!(POLL.load(), 1);
136 assert_eq!(SCHEDULE.load(), 0);
137 assert_eq!(DROP_F.load(), 1);
138 assert_eq!(DROP_S.load(), 0);
139 assert_eq!(DROP_D.load(), 0);
140 assert_eq!(DROP_O.load(), 1);
141 });
142
143 thread::sleep(ms(100));
144
145 handle.cancel();
146 assert_eq!(POLL.load(), 1);
147 assert_eq!(SCHEDULE.load(), 0);
148 assert_eq!(DROP_F.load(), 0);
149 assert_eq!(DROP_S.load(), 0);
150 assert_eq!(DROP_D.load(), 0);
151 assert_eq!(DROP_O.load(), 0);
152
153 thread::sleep(ms(200));
154
155 assert_eq!(POLL.load(), 1);
156 assert_eq!(SCHEDULE.load(), 0);
157 assert_eq!(DROP_F.load(), 1);
158 assert_eq!(DROP_S.load(), 0);
159 assert_eq!(DROP_D.load(), 0);
160 assert_eq!(DROP_O.load(), 1);
161
162 drop(handle);
163 assert_eq!(POLL.load(), 1);
164 assert_eq!(SCHEDULE.load(), 0);
165 assert_eq!(DROP_F.load(), 1);
166 assert_eq!(DROP_S.load(), 1);
167 assert_eq!(DROP_D.load(), 1);
168 assert_eq!(DROP_O.load(), 1);
169 })
170 .unwrap();
171}
172
173#[test]
174fn join_during_run() {
175 future!(f, POLL, DROP_F, DROP_O);
176 schedule!(s, SCHEDULE, DROP_S);
177 task!(task, handle, f, s, DROP_D);
178
179 crossbeam::scope(|scope| {
180 scope.spawn(|_| {
181 task.run();
182 assert_eq!(POLL.load(), 1);
183 assert_eq!(SCHEDULE.load(), 0);
184 assert_eq!(DROP_F.load(), 1);
185
186 thread::sleep(ms(100));
187 assert_eq!(DROP_S.load(), 1);
188 assert_eq!(DROP_D.load(), 1);
189 });
190
191 thread::sleep(ms(100));
192
193 assert!(block_on(handle).is_some());
194 assert_eq!(POLL.load(), 1);
195 assert_eq!(SCHEDULE.load(), 0);
196 assert_eq!(DROP_F.load(), 1);
197 assert_eq!(DROP_O.load(), 1);
198
199 thread::sleep(ms(100));
200 assert_eq!(DROP_S.load(), 1);
201 assert_eq!(DROP_D.load(), 1);
202 })
203 .unwrap();
204}
205
206#[test]
207fn try_join_during_run() {
208 future!(f, POLL, DROP_F, DROP_O);
209 schedule!(s, SCHEDULE, DROP_S);
210 task!(task, mut handle, f, s, DROP_D);
211
212 crossbeam::scope(|scope| {
213 scope.spawn(|_| {
214 task.run();
215 assert_eq!(POLL.load(), 1);
216 assert_eq!(SCHEDULE.load(), 0);
217 assert_eq!(DROP_F.load(), 1);
218 assert_eq!(DROP_S.load(), 1);
219 assert_eq!(DROP_D.load(), 1);
220 assert_eq!(DROP_O.load(), 1);
221 });
222
223 thread::sleep(ms(100));
224
225 block_on(future::select(&mut handle, future::ready(())));
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);
230 assert_eq!(DROP_D.load(), 0);
231 assert_eq!(DROP_O.load(), 0);
232 drop(handle);
233 })
234 .unwrap();
235}
236
237#[test]
238fn drop_handle_during_run() {
239 future!(f, POLL, DROP_F, DROP_O);
240 schedule!(s, SCHEDULE, DROP_S);
241 task!(task, handle, f, s, DROP_D);
242
243 crossbeam::scope(|scope| {
244 scope.spawn(|_| {
245 task.run();
246 assert_eq!(POLL.load(), 1);
247 assert_eq!(SCHEDULE.load(), 0);
248 assert_eq!(DROP_F.load(), 1);
249 assert_eq!(DROP_S.load(), 1);
250 assert_eq!(DROP_D.load(), 1);
251 assert_eq!(DROP_O.load(), 1);
252 });
253
254 thread::sleep(ms(100));
255
256 drop(handle);
257 assert_eq!(POLL.load(), 1);
258 assert_eq!(SCHEDULE.load(), 0);
259 assert_eq!(DROP_F.load(), 0);
260 assert_eq!(DROP_S.load(), 0);
261 assert_eq!(DROP_D.load(), 0);
262 assert_eq!(DROP_O.load(), 0);
263 })
264 .unwrap();
265}