1 use async_task::{Builder, Runnable};
2 use flume::unbounded;
3 use smol::future;
4 
5 use std::sync::atomic::{AtomicUsize, Ordering};
6 
7 #[test]
metadata_use_case()8 fn metadata_use_case() {
9     // Each future has a counter that is incremented every time it is scheduled.
10     let (sender, receiver) = unbounded::<Runnable<AtomicUsize>>();
11     let schedule = move |runnable: Runnable<AtomicUsize>| {
12         runnable.metadata().fetch_add(1, Ordering::SeqCst);
13         sender.send(runnable).ok();
14     };
15 
16     async fn my_future(counter: &AtomicUsize) {
17         loop {
18             // Loop until we've been scheduled five times.
19             let count = counter.load(Ordering::SeqCst);
20             if count < 5 {
21                 // Make sure that we are immediately scheduled again.
22                 future::yield_now().await;
23                 continue;
24             }
25 
26             // We've been scheduled five times, so we're done.
27             break;
28         }
29     }
30 
31     let make_task = || {
32         // SAFETY: We are spawning a non-'static future, so we need to use the unsafe API.
33         // The borrowed variables, in this case the metadata, are guaranteed to outlive the runnable.
34         let (runnable, task) = unsafe {
35             Builder::new()
36                 .metadata(AtomicUsize::new(0))
37                 .spawn_unchecked(my_future, schedule.clone())
38         };
39 
40         runnable.schedule();
41         task
42     };
43 
44     // Make tasks.
45     let t1 = make_task();
46     let t2 = make_task();
47 
48     // Run the tasks.
49     while let Ok(runnable) = receiver.try_recv() {
50         runnable.run();
51     }
52 
53     // Unwrap the tasks.
54     smol::future::block_on(async move {
55         t1.await;
56         t2.await;
57     });
58 }
59