use core::cell::UnsafeCell;
use core::sync::atomic::AtomicU8;
use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release};
use core::task::Poll;
use core::task::Waker;
pub type AtomicWaker = UnsafeCell<Option<Waker>>;
pub type AtomicWakerState = AtomicU8;
const IDLING: u8 = 0;
const REGISTERING: u8 = 0b001;
const WAKING: u8 = 0b010;
const AWOKEN: u8 = 0b100;
pub fn poll_const(atomic_waker: &AtomicWaker, state: &AtomicWakerState, waker: &Waker) -> Poll<()> {
match state.fetch_or(REGISTERING, Acquire) {
IDLING => {
match unsafe { &mut *atomic_waker.get() } {
Some(old_waker) if old_waker.will_wake(waker) => (),
new_waker => *new_waker = Some(waker.clone()),
}
let prev = state.swap(IDLING, AcqRel);
if prev & WAKING != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
}
prev if prev & REGISTERING != 0 => {
Poll::Pending
}
prev if prev & AWOKEN != 0 => {
debug_assert!(prev == AWOKEN || prev == WAKING | AWOKEN);
let _ = state.swap(IDLING, AcqRel);
Poll::Ready(())
}
prev => {
debug_assert!(prev == WAKING);
const SPIN_LIMIT: usize = 16;
for _ in 0..SPIN_LIMIT {
let cur = state.load(Acquire);
if cur & AWOKEN != 0 {
if state
.compare_exchange(cur & !REGISTERING, IDLING, AcqRel, Relaxed)
.is_ok()
{
return Poll::Ready(());
} else {
break;
}
}
if cur & WAKING == 0 {
break;
}
core::hint::spin_loop();
}
let _ = state.compare_exchange(WAKING | REGISTERING, WAKING, AcqRel, Relaxed);
waker.wake_by_ref();
Poll::Pending
}
}
}
pub fn wake(atomic_waker: &AtomicWaker, state: &AtomicWakerState) {
match state.fetch_or(WAKING, AcqRel) {
IDLING => {
let waker = unsafe { (*atomic_waker.get()).take() };
state.store(AWOKEN, Release);
if let Some(waker) = waker {
waker.wake();
}
}
_ => {
}
}
}