|  | //! A simple single-threaded executor that can spawn non-`Send` futures. | 
|  |  | 
|  | use std::cell::Cell; | 
|  | use std::future::Future; | 
|  | use std::rc::Rc; | 
|  |  | 
|  | use crossbeam::channel::{unbounded, Receiver, Sender}; | 
|  |  | 
|  | type Task = async_task::Task<()>; | 
|  | type JoinHandle<T> = async_task::JoinHandle<T, ()>; | 
|  |  | 
|  | thread_local! { | 
|  | // A channel that holds scheduled tasks. | 
|  | static QUEUE: (Sender<Task>, Receiver<Task>) = unbounded(); | 
|  | } | 
|  |  | 
|  | /// Spawns a future on the executor. | 
|  | fn spawn<F, R>(future: F) -> JoinHandle<R> | 
|  | where | 
|  | F: Future<Output = R> + 'static, | 
|  | R: 'static, | 
|  | { | 
|  | // Create a task that is scheduled by sending itself into the channel. | 
|  | let schedule = |t| QUEUE.with(|(s, _)| s.send(t).unwrap()); | 
|  | let (task, handle) = async_task::spawn_local(future, schedule, ()); | 
|  |  | 
|  | // Schedule the task by sending it into the queue. | 
|  | task.schedule(); | 
|  |  | 
|  | handle | 
|  | } | 
|  |  | 
|  | /// Runs a future to completion. | 
|  | fn run<F, R>(future: F) -> R | 
|  | where | 
|  | F: Future<Output = R> + 'static, | 
|  | R: 'static, | 
|  | { | 
|  | // Spawn a task that sends its result through a channel. | 
|  | let (s, r) = unbounded(); | 
|  | spawn(async move { s.send(future.await).unwrap() }); | 
|  |  | 
|  | loop { | 
|  | // If the original task has completed, return its result. | 
|  | if let Ok(val) = r.try_recv() { | 
|  | return val; | 
|  | } | 
|  |  | 
|  | // Otherwise, take a task from the queue and run it. | 
|  | QUEUE.with(|(_, r)| r.recv().unwrap().run()); | 
|  | } | 
|  | } | 
|  |  | 
|  | fn main() { | 
|  | let val = Rc::new(Cell::new(0)); | 
|  |  | 
|  | // Run a future that increments a non-`Send` value. | 
|  | run({ | 
|  | let val = val.clone(); | 
|  | async move { | 
|  | // Spawn a future that increments the value. | 
|  | let handle = spawn({ | 
|  | let val = val.clone(); | 
|  | async move { | 
|  | val.set(dbg!(val.get()) + 1); | 
|  | } | 
|  | }); | 
|  |  | 
|  | val.set(dbg!(val.get()) + 1); | 
|  | handle.await; | 
|  | } | 
|  | }); | 
|  |  | 
|  | // The value should be 2 at the end of the program. | 
|  | dbg!(val.get()); | 
|  | } |