#![allow(unsafe_code)]
use core::cell::Cell;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::task::Wake;
struct Counter(AtomicUsize);
impl Wake for Counter {
fn wake(self: Arc<Self>) {
self.0.fetch_add(1, Ordering::SeqCst);
}
fn wake_by_ref(self: &Arc<Self>) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
pub(crate) struct Polled<F: Future> {
future: Pin<Box<F>>,
counter: Arc<Counter>,
waker: Waker,
}
impl<F: Future> Polled<F> {
pub(crate) fn new(future: F) -> Self {
let counter = Arc::new(Counter(AtomicUsize::new(0)));
let waker = Waker::from(Arc::clone(&counter));
Self {
future: Box::pin(future),
counter,
waker,
}
}
pub(crate) fn poll(&mut self) -> Poll<F::Output> {
self.future
.as_mut()
.poll(&mut Context::from_waker(&self.waker))
}
pub(crate) fn pending(&mut self) {
assert!(self.poll().is_pending(), "the future completed early");
}
pub(crate) fn ready(&mut self) -> F::Output {
match self.poll() {
Poll::Ready(output) => output,
Poll::Pending => panic!("the future is still pending"),
}
}
pub(crate) fn wakes(&self) -> usize {
self.counter.0.load(Ordering::SeqCst)
}
pub(crate) fn waker(&self) -> Waker {
self.waker.clone()
}
}
struct Hook {
on_clone: Cell<Option<Box<dyn FnOnce()>>>,
on_wake: Cell<Option<Box<dyn FnOnce()>>>,
}
static HOOK: RawWakerVTable =
RawWakerVTable::new(hook_clone, hook_wake, hook_wake_by_ref, hook_drop);
unsafe fn hook_clone(data: *const ()) -> RawWaker {
let hook = unsafe { &*(data as *const Hook) };
if let Some(run) = hook.on_clone.take() {
run();
}
unsafe { Rc::increment_strong_count(data as *const Hook) };
RawWaker::new(data, &HOOK)
}
unsafe fn hook_wake(data: *const ()) {
let hook = unsafe { Rc::from_raw(data as *const Hook) };
if let Some(run) = hook.on_wake.take() {
run();
}
}
unsafe fn hook_wake_by_ref(data: *const ()) {
let hook = unsafe { &*(data as *const Hook) };
if let Some(run) = hook.on_wake.take() {
run();
}
}
unsafe fn hook_drop(data: *const ()) {
drop(unsafe { Rc::from_raw(data as *const Hook) });
}
pub(crate) fn hooked(
on_clone: Option<Box<dyn FnOnce()>>,
on_wake: Option<Box<dyn FnOnce()>>,
) -> Waker {
let hook = Rc::new(Hook {
on_clone: Cell::new(on_clone),
on_wake: Cell::new(on_wake),
});
let data = Rc::into_raw(hook) as *const ();
unsafe { Waker::from_raw(RawWaker::new(data, &HOOK)) }
}
static DROP_PANICS: RawWakerVTable = RawWakerVTable::new(
drop_panics_clone,
drop_panics_wake,
drop_panics_noop,
drop_panics_drop,
);
unsafe fn drop_panics_clone(data: *const ()) -> RawWaker {
unsafe { Arc::increment_strong_count(data as *const AtomicBool) };
RawWaker::new(data, &DROP_PANICS)
}
unsafe fn drop_panics_wake(data: *const ()) {
drop(unsafe { Arc::from_raw(data as *const AtomicBool) });
}
unsafe fn drop_panics_noop(_: *const ()) {}
unsafe fn drop_panics_drop(data: *const ()) {
let armed = unsafe { Arc::from_raw(data as *const AtomicBool) };
if armed.swap(false, Ordering::SeqCst) {
panic!("a waker's drop panicked");
}
}
pub(crate) fn drop_panics() -> Waker {
let armed = Arc::into_raw(Arc::new(AtomicBool::new(true)));
unsafe { Waker::from_raw(RawWaker::new(armed.cast(), &DROP_PANICS)) }
}
pub(crate) fn with_budget_spent<R>(poll: impl FnOnce() -> R) -> R {
let saved = crate::coop::enter();
while crate::coop::try_spend() {}
let result = poll();
crate::coop::leave(saved);
result
}
pub(crate) use super::internal::block_on;