use core::task::{RawWakerVTable, Waker};
use super::abort_on_panic;
use super::raw::TaskVTable;
use super::state::{AWAITER, NOTIFYING, REFERENCE, REGISTERING, SCHEDULED, TASK};
use crate::sync::internal::{AtomicPtr, AtomicUsize, Ordering, UnsafeCell};
pub(super) struct Header {
pub(super) state: AtomicUsize,
awaiter: UnsafeCell<Option<Waker>>,
awaiter_data: AtomicPtr<()>,
awaiter_table: AtomicPtr<RawWakerVTable>,
pub(super) vtable: &'static TaskVTable,
pub(super) id: super::Id,
pub(super) link: crate::rt::inbox::Link,
pub(super) owner: crate::mem::Owner,
}
static NEXT_ID: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(1);
impl Header {
pub(super) fn new(vtable: &'static TaskVTable, owner: crate::mem::Owner) -> Self {
let id = NEXT_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
Self {
state: AtomicUsize::new(SCHEDULED | TASK | REFERENCE),
awaiter: UnsafeCell::new(None),
awaiter_data: AtomicPtr::new(core::ptr::null_mut()),
awaiter_table: AtomicPtr::new(core::ptr::null_mut()),
vtable,
id: super::Id(core::num::NonZeroU64::new(id).unwrap_or(core::num::NonZeroU64::MIN)),
link: crate::rt::inbox::Link::new(),
owner,
}
}
pub(super) fn notify(&self, current: Option<&Waker>) {
if let Some(waker) = self.take(current) {
abort_on_panic(|| waker.wake());
}
}
#[inline]
pub(super) fn take(&self, current: Option<&Waker>) -> Option<Waker> {
let state = self.state.fetch_or(NOTIFYING, Ordering::AcqRel);
if state & (NOTIFYING | REGISTERING) != 0 {
return None;
}
let waker = self.awaiter.with_mut(|cell| unsafe { (*cell).take() });
self.state
.fetch_and(!NOTIFYING & !AWAITER, Ordering::Release);
match (waker, current) {
(Some(waker), Some(current)) if waker.will_wake(current) => {
abort_on_panic(|| drop(waker));
None
}
(waker, _) => waker,
}
}
#[inline(always)]
pub(super) fn claim(&self) -> (Option<Waker>, bool, usize) {
let state = self.state.fetch_or(NOTIFYING, Ordering::AcqRel);
if state & (NOTIFYING | REGISTERING) != 0 {
return (None, false, state | NOTIFYING);
}
let waker = self.awaiter.with_mut(|cell| unsafe { (*cell).take() });
(waker, true, state | NOTIFYING)
}
#[inline]
fn stored_wakes_as(&self, waker: &Waker) -> bool {
core::ptr::eq(self.awaiter_data.load(Ordering::Relaxed), waker.data())
&& core::ptr::eq(self.awaiter_table.load(Ordering::Relaxed), waker.vtable())
}
#[inline(always)]
pub(super) fn register(&self, waker: &Waker) {
let mut state = self.state.load(Ordering::Acquire);
loop {
debug_assert!(state & REGISTERING == 0, "one awaiter registers at a time");
if state & NOTIFYING != 0 {
if state & AWAITER == 0 || !self.stored_wakes_as(waker) {
abort_on_panic(|| waker.wake_by_ref());
}
return;
}
match self.state.compare_exchange_weak(
state,
state | REGISTERING,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
state |= REGISTERING;
break;
}
Err(actual) => state = actual,
}
}
let old = self
.awaiter
.with_mut(|cell| unsafe { (*cell).replace(waker.clone()) });
self.awaiter_data
.store(waker.data().cast_mut(), Ordering::Relaxed);
self.awaiter_table.store(
core::ptr::from_ref(waker.vtable()).cast_mut(),
Ordering::Relaxed,
);
abort_on_panic(|| drop(old));
let mut missed = None;
loop {
if state & NOTIFYING != 0 && missed.is_none() {
missed = self.awaiter.with_mut(|cell| unsafe { (*cell).take() });
}
let new = if missed.is_none() {
(state & !NOTIFYING & !REGISTERING) | AWAITER
} else {
state & !NOTIFYING & !REGISTERING & !AWAITER
};
match self
.state
.compare_exchange_weak(state, new, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => break,
Err(actual) => state = actual,
}
}
if let Some(waker) = missed {
abort_on_panic(|| waker.wake());
}
}
}