blob: 4e66c32c7a8866db84bd560a9100619d0ba2c37f [file] [log] [blame]
//! 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());
}