use std::{
future::Future,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
task::{Context, Poll, Wake},
thread::{self, Thread},
};
pub fn simple_block_on<F: Future>(f: F) -> F::Output {
let _enter = enter();
let thread = ThreadWaker::new_arc();
let waker = thread.clone().into();
let mut cx = Context::from_waker(&waker);
let mut f = Box::pin(f);
loop {
match f.as_mut().poll(&mut cx) {
Poll::Ready(r) => return r,
Poll::Pending => thread.park(),
}
}
}
thread_local! {
static BUSY: AtomicBool = const { AtomicBool::new(false) };
}
struct EnterGuard;
impl Drop for EnterGuard {
fn drop(&mut self) {
BUSY.with(|e| e.swap(false, Ordering::Acquire));
}
}
fn enter() -> EnterGuard {
if BUSY.with(|e| e.swap(true, Ordering::Release)) {
panic!("Cannot call simple_block_on recursively")
}
EnterGuard
}
struct ThreadWaker {
thread: Thread,
parked: AtomicBool,
}
impl ThreadWaker {
fn new_arc() -> Arc<Self> {
Arc::new(Self {
thread: thread::current(),
parked: AtomicBool::new(true),
})
}
fn park(&self) {
if !self.parked.swap(true, Ordering::Acquire) {
thread::park();
}
}
fn unpark(&self) {
if self.parked.swap(false, Ordering::Release) {
self.thread.unpark();
}
}
}
impl Wake for ThreadWaker {
fn wake(self: Arc<Self>) {
self.unpark()
}
fn wake_by_ref(self: &Arc<Self>) {
self.unpark()
}
}
#[cfg(test)]
mod test {
use super::*;
use std::future;
#[test]
fn simple() {
assert_eq!(simple_block_on(future::ready(42)), 42);
}
#[test]
fn poll_fn() {
let mut a = 0;
let fut = future::poll_fn(move |cx| {
if a == 5 {
return Poll::Ready(10);
}
a += 1;
cx.waker().wake_by_ref();
Poll::Pending
});
assert_eq!(simple_block_on(fut), 10);
}
}