use super::Stats;
use alloc::sync::Arc;
use core::future::Future;
use core::mem::ManuallyDrop;
use core::pin::Pin;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use core::sync::atomic::{AtomicU16, Ordering::Relaxed};
pub struct Wookie<F> {
wakey: Arc<Wakey>,
ptr: *const Wakey,
future: F,
}
impl<F: Future> Wookie<F> {
#[inline(always)]
pub fn new(future: F) -> Wookie<F> {
let ptr = Arc::into_raw(Arc::new(Wakey::default()));
let wakey = unsafe { Arc::from_raw(ptr) };
Wookie { wakey, ptr, future }
}
#[inline(always)]
pub fn woken(self: &mut Pin<&mut Self>) -> u16 {
self.as_mut().project().wakey.woken.load(Relaxed)
}
#[inline(always)]
pub fn cloned(self: &mut Pin<&mut Self>) -> u16 {
self.as_mut().project().wakey.cloned.load(Relaxed)
}
#[inline(always)]
pub fn dropped(self: &mut Pin<&mut Self>) -> u16 {
self.as_mut().project().wakey.dropped.load(Relaxed)
}
#[inline(always)]
pub fn stats(self: &mut Pin<&mut Self>) -> Stats {
let wakey = self.as_mut().project().wakey.as_ref();
Stats {
cloned: wakey.cloned.load(Relaxed),
dropped: wakey.dropped.load(Relaxed),
woken: wakey.woken.load(Relaxed),
}
}
#[inline(always)]
pub fn live(self: &mut Pin<&mut Self>) -> u16 {
let wakey = self.as_mut().project().wakey.as_ref();
wakey.cloned.load(Relaxed) - wakey.dropped.load(Relaxed)
}
#[inline(always)]
pub fn poll(
self: &mut Pin<&mut Self>
) -> Poll<<F as Future>::Output> {
let this = self.as_mut().project();
let waker = ManuallyDrop::new(this.waker());
let future = unsafe { Pin::new_unchecked(&mut this.future) };
let mut ctx = Context::from_waker(&waker);
Future::poll(future, &mut ctx)
}
#[inline(always)]
pub fn poll_while_woken(
self: &mut Pin<&mut Self>
) -> Poll<<F as Future>::Output> {
let mut woken = self.woken();
loop {
if let Poll::Ready(r) = self.poll() { return Poll::Ready(r); }
let w = self.woken();
if w == woken { return Poll::Pending; }
woken = w;
}
}
#[inline(always)]
fn waker(&self) -> Waker {
let raw = wookie_rawwaker(self.ptr);
unsafe { Waker::from_raw(raw) }
}
#[inline(always)]
fn project(self: Pin<&mut Self>) -> &mut Self {
unsafe { Pin::into_inner_unchecked(self) }
}
}
#[macro_export]
macro_rules! wookie {
($name:ident) => {
let mut $name = unsafe { $crate::Wookie::new($name) };
#[allow(unused_mut)]
let mut $name = unsafe { core::pin::Pin::new_unchecked(&mut $name) };
};
($name:ident : $future:expr) => {
let mut $name = unsafe { $crate::Wookie::new($future) };
#[allow(unused_mut)]
let mut $name = unsafe { core::pin::Pin::new_unchecked(&mut $name) };
}
}
#[derive(Default)]
struct Wakey {
cloned: AtomicU16,
dropped: AtomicU16,
woken: AtomicU16,
}
impl Wakey {
fn bump_cloned(&self) -> u16 { self.cloned.fetch_add(1, Relaxed) }
fn bump_woken(&self) -> u16 { self.woken.fetch_add(1, Relaxed) }
fn bump_dropped(&self) -> u16 { self.dropped.fetch_add(1, Relaxed) }
}
fn wookie_rawwaker(wakey: *const Wakey) -> RawWaker {
fn do_clone(data: *const ()) -> RawWaker {
let wakey = data as *const Wakey;
unsafe { &*wakey }.bump_cloned();
unsafe { Arc::increment_strong_count(wakey) };
wookie_rawwaker(wakey)
}
fn do_wake(data: *const ()) {
let wakey: Arc<Wakey> = unsafe { Arc::from_raw(data as *const Wakey) };
wakey.bump_woken();
wakey.bump_dropped();
}
fn do_wake_by_ref(data: *const ()) {
let arc = unsafe { Arc::from_raw(data as *const Wakey) };
let wakey = ManuallyDrop::new(arc);
wakey.bump_woken();
}
fn do_drop(data: *const ()) {
let wakey: Arc<Wakey> = unsafe { Arc::from_raw(data as *const Wakey) };
wakey.bump_dropped();
}
RawWaker::new(
wakey as *const (),
&RawWakerVTable::new(do_clone, do_wake, do_wake_by_ref, do_drop)
)
}