use alloc::sync::Arc;
use core::{
sync::atomic::{AtomicU32, Ordering},
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
pub fn block_on<F: Future>(future: F) -> F::Output {
let mut future = core::pin::pin!(future);
let unparker = Unparker::default();
let waker = unparker.clone().into();
let mut context = Context::from_waker(&waker);
loop {
if let Poll::Ready(result) = future.as_mut().poll(&mut context) {
break result;
}
unparker.park();
}
}
#[derive(Clone, Debug)]
#[repr(transparent)]
struct Unparker {
raw: Arc<AtomicU32>,
}
impl Default for Unparker {
fn default() -> Self {
Self {
raw: Arc::new(AtomicU32::new(1)),
}
}
}
impl Unparker {
fn park(&self) {
let prev = self.raw.swap(1, Ordering::Relaxed);
if prev == 1 {
atomic_wait::wait(&self.raw, 1);
}
}
}
impl From<Unparker> for Waker {
fn from(value: Unparker) -> Self {
let data = Arc::into_raw(value.raw);
let raw_waker =
RawWaker::new(data.cast::<()>(), &Unparker::WAKER_VTABLE);
unsafe { Waker::from_raw(raw_waker) }
}
}
impl Unparker {
const WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
Self::waker_clone,
Self::waker_wake,
Self::waker_wake_by_ref,
Self::waker_drop,
);
unsafe fn waker_clone(data: *const ()) -> RawWaker {
let data = data.cast::<AtomicU32>();
unsafe { Arc::increment_strong_count(data) };
RawWaker::new(data.cast::<()>(), &Self::WAKER_VTABLE)
}
unsafe fn waker_wake(data: *const ()) {
let data = data.cast::<AtomicU32>();
let data = unsafe { Arc::from_raw(data) };
let value = data.swap(0, Ordering::Relaxed);
if value != 0 {
atomic_wait::wake_one(&*data);
}
}
unsafe fn waker_wake_by_ref(data: *const ()) {
let data = data.cast::<AtomicU32>();
let data = unsafe { &*data };
let value = data.swap(0, Ordering::Relaxed);
if value != 0 {
atomic_wait::wake_one(data);
}
}
unsafe fn waker_drop(data: *const ()) {
let data = data.cast::<AtomicU32>();
unsafe { Arc::decrement_strong_count(data) };
}
}