async-std
and smol
async-task
& futures-lite
use std::{future::Future, panic::catch_unwind, thread};
use async_task::{Runnable, Task};
use futures_lite::future;
use once_cell::sync::Lazy;
fn main() {
let t = spawn(async {
println!("Hello task!");
});
future::block_on(t);
}
fn spawn<F, T>(future: F) -> Task<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
static queue: Lazy<flume::Sender<Runnable>> = Lazy::new(|| {
let (tx, rx) = flume::unbounded::<Runnable>();
thread::spawn(move || {
while let Ok(runnable) = rx.recv() {
let _ = catch_unwind(|| runnable.run());
}
});
tx
});
let schedule = |runnable| queue.send(runnable).unwrap();
let (runnable, task) = async_task::spawn(future, schedule);
runnable.schedule();
return task;
}
block_on
use parking::Parker;
use waker_fn::waker_fn;
pub fn block_on<T>(future: impl Future<Output = T>) -> T {
crate::pin!(future);
thread_local! {
// Cached parker and waker for efficiency.
static CACHE: RefCell<(Parker, Waker)> = RefCell::new(parker_and_waker());
}
// ...
}
block_on
nesting…match cache.try_borrow_mut() {
// Use the cached parker and waker.
Ok(cache) => {
let (parker, waker) = &*cache;
let cx = &mut Context::from_waker(&waker);
loop {
match future.as_mut().poll(cx) {
Poll::Ready(output) => return output,
Poll::Pending => parker.park(),
}
}
}
_ => todo!()
}
block_on
nestingmatch cache.try_borrow_mut() {
// Looks like this is a recursive `block_on()` call.
Err(_) => {
let (parker, waker) = parker_and_waker();
let cx = &mut Context::from_waker(&waker);
loop {
match future.as_mut().poll(cx) {
Poll::Ready(output) => return output,
Poll::Pending => parker.park(),
}
}
}
_ => todo!()
}
Parker
comes from the parking
crateWaker
is part of core::task
waker_fn
is a convenience builder crate for Waker
fn parker_and_waker() -> (Parker, Waker) {
let parker = Parker::new();
let unparker = parker.unparker();
let waker = waker_fn(move || {
unparker.unpark();
});
(parker, waker)
}
Isn't this cool??
fn main() {
// allow us to attach futures to our state machine after it
// started running
let t = spawn(async {
println!("Hello task!");
});
// blocks the current thread until all futures are done
block_on(t);
}
async-std
We can easily create tasks without the syntactic sugar
use async_std::task;
async fn hello() {
println!("Hello world!");
}
fn main() {
task::block_on(hello());
}
tokio
We can easily create tasks without the syntactic sugar
use tokio::{runtime::Runtime, task};
async fn hello() {
println!("Hello world!");
}
fn main() {
let rt = Runtime::new().unwrap();
task::block_in_place(move || {
let local = task::LocalSet::new();
local.block_on(&rt, hello());
})
}