blob: 4e66c32c7a8866db84bd560a9100619d0ba2c37f [file] [log] [blame]
Stjepan Glavinafcfa4ab2019-11-25 18:39:17 +01001//! A simple single-threaded executor that can spawn non-`Send` futures.
2
3use std::cell::Cell;
4use std::future::Future;
5use std::rc::Rc;
6
7use crossbeam::channel::{unbounded, Receiver, Sender};
8
9type Task = async_task::Task<()>;
10type JoinHandle<T> = async_task::JoinHandle<T, ()>;
11
12thread_local! {
13 // A channel that holds scheduled tasks.
14 static QUEUE: (Sender<Task>, Receiver<Task>) = unbounded();
15}
16
17/// Spawns a future on the executor.
18fn spawn<F, R>(future: F) -> JoinHandle<R>
19where
20 F: Future<Output = R> + 'static,
21 R: 'static,
22{
23 // Create a task that is scheduled by sending itself into the channel.
24 let schedule = |t| QUEUE.with(|(s, _)| s.send(t).unwrap());
25 let (task, handle) = async_task::spawn_local(future, schedule, ());
26
27 // Schedule the task by sending it into the queue.
28 task.schedule();
29
30 handle
31}
32
33/// Runs a future to completion.
34fn run<F, R>(future: F) -> R
35where
36 F: Future<Output = R> + 'static,
37 R: 'static,
38{
39 // Spawn a task that sends its result through a channel.
40 let (s, r) = unbounded();
41 spawn(async move { s.send(future.await).unwrap() });
42
43 loop {
44 // If the original task has completed, return its result.
45 if let Ok(val) = r.try_recv() {
46 return val;
47 }
48
49 // Otherwise, take a task from the queue and run it.
50 QUEUE.with(|(_, r)| r.recv().unwrap().run());
51 }
52}
53
54fn main() {
55 let val = Rc::new(Cell::new(0));
56
57 // Run a future that increments a non-`Send` value.
58 run({
59 let val = val.clone();
60 async move {
61 // Spawn a future that increments the value.
62 let handle = spawn({
63 let val = val.clone();
64 async move {
65 val.set(dbg!(val.get()) + 1);
66 }
67 });
68
69 val.set(dbg!(val.get()) + 1);
70 handle.await;
71 }
72 });
73
74 // The value should be 2 at the end of the program.
75 dbg!(val.get());
76}