use core::task::Waker;
use super::abort_on_panic;
use super::raw::TaskVTable;
use super::state::{AWAITER, NOTIFYING, REFERENCE, REGISTERING, SCHEDULED, TASK};
use crate::sync::internal::{AtomicUsize, Ordering, UnsafeCell};
pub(super) struct Header {
pub(super) state: AtomicUsize,
awaiter: UnsafeCell<Option<Waker>>,
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),
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());
}
}
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,
}
}
pub(super) fn register(&self, waker: &Waker) {
let mut state = self.state.fetch_or(0, Ordering::Acquire);
loop {
debug_assert!(state & REGISTERING == 0, "one awaiter registers at a time");
if state & NOTIFYING != 0 {
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()) });
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());
}
}
}