#![allow(unsafe_code)]
use core::mem;
use core::ptr::{self, NonNull};
use core::task::Waker;
#[cfg(all(loom, test))]
use super::wait::Park;
use crate::sync::internal::{AtomicPtr, AtomicUsize, Ordering, UnsafeCell};
use crate::sync::queue::WaitQueue;
const PHASE: usize = 0b11;
const WAITING: usize = 0;
pub(super) const GRANTED: usize = 1;
const CANCELLED: usize = 2;
const REGISTERING: usize = 1 << 2;
const ARMED: usize = 1 << 3;
const TAKING: usize = 1 << 4;
const CURRENT: usize = 1 << 5;
const TAKEN: usize = 1 << 6;
pub(super) const NUDGED: usize = 1 << 7;
const OWED: usize = 1 << 8;
const QUEUE_REF: usize = 1 << 9;
pub(super) const FUTURE_REF: usize = 1 << 10;
const WAKE_REF: usize = 1 << 11;
const REFS: usize = QUEUE_REF | FUTURE_REF | WAKE_REF;
pub(in crate::sync) struct Waiter {
state: AtomicUsize,
next: AtomicPtr<Waiter>,
prev: AtomicPtr<Waiter>,
pub(super) wake_next: AtomicPtr<Waiter>,
payload: AtomicUsize,
slots: [UnsafeCell<Option<Waker>>; 2],
}
pub(super) enum Touch {
Claimed,
Marked,
Pending,
Skipped,
}
struct Settle<'a>(&'a AtomicUsize);
impl Drop for Settle<'_> {
#[cold]
fn drop(&mut self) {
let mut state = self.0.load(Ordering::Acquire);
loop {
let next = (state & !REGISTERING) | ARMED;
match self
.0
.compare_exchange_weak(state, next, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => return,
Err(actual) => state = actual,
}
}
}
}
pub(super) enum Seen {
Granted,
Nudged,
Pending,
}
fn claim(state: usize) -> usize {
let taken = if state & CURRENT != 0 { TAKEN } else { 0 };
(state & !(ARMED | TAKEN)) | taken | TAKING | WAKE_REF
}
fn claimable(state: usize) -> bool {
state & (ARMED | REGISTERING | TAKING) == ARMED
}
impl Waiter {
pub(super) fn new() -> Self {
Self {
state: AtomicUsize::new(0),
next: AtomicPtr::new(ptr::null_mut()),
prev: AtomicPtr::new(ptr::null_mut()),
wake_next: AtomicPtr::new(ptr::null_mut()),
payload: AtomicUsize::new(0),
slots: [UnsafeCell::new(None), UnsafeCell::new(None)],
}
}
pub(in crate::sync) fn next(&self) -> *mut Waiter {
self.next.load(Ordering::Relaxed)
}
pub(in crate::sync) fn set_next(&self, next: *mut Waiter) {
self.next.store(next, Ordering::Relaxed);
}
pub(in crate::sync) fn prev(&self) -> *mut Waiter {
self.prev.load(Ordering::Relaxed)
}
pub(in crate::sync) fn set_prev(&self, prev: *mut Waiter) {
self.prev.store(prev, Ordering::Relaxed);
}
pub(in crate::sync) fn payload(&self) -> usize {
self.payload.load(Ordering::Relaxed)
}
pub(in crate::sync) fn set_payload(&self, payload: usize) {
self.payload.store(payload, Ordering::Relaxed);
}
pub(in crate::sync) fn is_cancelled(&self) -> bool {
self.state.load(Ordering::Acquire) & PHASE == CANCELLED
}
pub(super) fn is_granted(&self) -> bool {
self.state.load(Ordering::Acquire) & PHASE == GRANTED
}
fn is_owed(&self) -> bool {
self.state.load(Ordering::Acquire) & OWED != 0
}
fn slot(&self, state: usize, bit: usize) -> &UnsafeCell<Option<Waker>> {
&self.slots[usize::from(state & bit != 0)]
}
pub(super) fn arm(&self, payload: usize, waker: Waker) {
self.state.store(
WAITING | REGISTERING | QUEUE_REF | FUTURE_REF,
Ordering::Relaxed,
);
self.next.store(ptr::null_mut(), Ordering::Relaxed);
self.prev.store(ptr::null_mut(), Ordering::Relaxed);
self.wake_next.store(ptr::null_mut(), Ordering::Relaxed);
self.payload.store(payload, Ordering::Relaxed);
self.slots[0].with_mut(|slot| unsafe { *slot = Some(waker) });
}
pub(super) fn touch(&self, bit: usize, once: bool) -> Touch {
let mut state = self.state.load(Ordering::Acquire);
loop {
if state & PHASE != WAITING {
return Touch::Skipped;
}
if once && state & bit != 0 {
return Touch::Pending;
}
let take = claimable(state);
let next = if take {
claim(state) | bit
} else {
state | bit
};
match self
.state
.compare_exchange_weak(state, next, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) if take => return Touch::Claimed,
Ok(_) => return Touch::Marked,
Err(actual) => state = actual,
}
}
}
pub(super) fn park(&self, waker: Option<&Waker>) -> Seen {
let mut stale = None;
if let Some(waker) = waker {
let mut state = self.state.load(Ordering::Acquire);
let target = loop {
if state & PHASE == GRANTED {
return Seen::Granted;
}
if state & NUDGED != 0 {
match self.state.compare_exchange_weak(
state,
state & !NUDGED,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return Seen::Nudged,
Err(actual) => {
state = actual;
continue;
}
}
}
let current = if state & TAKING != 0 {
!state & TAKEN != 0
} else {
state & CURRENT != 0
};
let next =
(state & !(ARMED | CURRENT)) | REGISTERING | if current { CURRENT } else { 0 };
match self.state.compare_exchange_weak(
state,
next,
Ordering::Acquire,
Ordering::Acquire,
) {
Ok(_) => break next,
Err(actual) => state = actual,
}
};
stale = self.slot(target, CURRENT).with_mut(|slot| unsafe {
if (*slot).as_ref().is_some_and(|w| w.will_wake(waker)) {
None
} else {
let settle = Settle(&self.state);
let fresh = waker.clone();
mem::forget(settle);
(*slot).replace(fresh)
}
});
}
let mut state = self.state.load(Ordering::Acquire);
let seen = loop {
let next = (state & !(REGISTERING | NUDGED)) | ARMED;
match self
.state
.compare_exchange_weak(state, next, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) if state & PHASE == GRANTED => break Seen::Granted,
Ok(_) if state & NUDGED != 0 => break Seen::Nudged,
Ok(_) => break Seen::Pending,
Err(actual) => state = actual,
}
};
drop(stale);
seen
}
pub(super) fn begin_spin(&self) -> bool {
let mut state = self.state.load(Ordering::Acquire);
loop {
if state & PHASE == GRANTED || state & NUDGED != 0 {
return false;
}
if state & ARMED == 0 {
return true;
}
match self.state.compare_exchange_weak(
state,
state & !ARMED,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return true,
Err(actual) => state = actual,
}
}
}
pub(super) fn marked(&self) -> bool {
let state = self.state.load(Ordering::Relaxed);
state & PHASE == GRANTED || state & NUDGED != 0
}
pub(super) fn owe(&self) -> bool {
let mut state = self.state.load(Ordering::Acquire);
loop {
if state & PHASE != WAITING {
return false;
}
match self.state.compare_exchange_weak(
state,
state | OWED,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return true,
Err(actual) => state = actual,
}
}
}
pub(super) fn unowe(&self) -> bool {
let mut state = self.state.load(Ordering::Acquire);
loop {
if state & PHASE != WAITING || state & OWED == 0 {
return false;
}
match self.state.compare_exchange_weak(
state,
state & !OWED,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return true,
Err(actual) => state = actual,
}
}
}
pub(super) fn withdraw(&self) -> Option<(Option<Waker>, bool)> {
let mut state = self.state.load(Ordering::Acquire);
loop {
if state & PHASE == GRANTED {
return None;
}
let armed = state & ARMED != 0;
let nudged = state & NUDGED != 0;
match self.state.compare_exchange_weak(
state,
(state & !ARMED) | CANCELLED,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) if armed => {
let waker = self.slot(state, CURRENT).with_mut(|slot| {
unsafe { (*slot).take() }
});
return Some((waker, nudged));
}
Ok(_) => return Some((None, nudged)),
Err(actual) => state = actual,
}
}
}
pub(super) fn take_claimed(&self) -> (Option<Waker>, Taken) {
let mut state = self.state.load(Ordering::Acquire);
let waker = self
.slot(state, TAKEN)
.with_mut(|slot| unsafe { (*slot).take() });
loop {
let again = state & (ARMED | REGISTERING) == ARMED
&& (state & NUDGED != 0 || state & PHASE == GRANTED);
let next = if again {
claim(state & !TAKING)
} else {
state & !(TAKING | WAKE_REF)
};
match self
.state
.compare_exchange_weak(state, next, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) if again => return (waker, Taken::Again),
Ok(_) => return (waker, Taken::Done(state & REFS == WAKE_REF)),
Err(actual) => state = actual,
}
}
}
pub(super) fn clear_slots(&self) {
for slot in &self.slots {
drop(slot.with_mut(|slot| unsafe { (*slot).take() }));
}
}
#[cfg(all(loom, test))]
pub(super) fn retire(&mut self) {
self.state.with_mut(|_| ());
self.next.with_mut(|_| ());
self.prev.with_mut(|_| ());
self.wake_next.with_mut(|_| ());
self.payload.with_mut(|_| ());
}
}
#[cfg(all(loom, test))]
impl Waiter {
pub(super) fn arm_without_look(&self, waker: &Waker) -> Park {
let mut state = self.state.load(Ordering::Acquire);
let target = loop {
let current = if state & TAKING != 0 {
!state & TAKEN != 0
} else {
state & CURRENT != 0
};
let next =
(state & !(ARMED | CURRENT)) | REGISTERING | if current { CURRENT } else { 0 };
match self.state.compare_exchange_weak(
state,
next,
Ordering::Acquire,
Ordering::Acquire,
) {
Ok(_) => break next,
Err(actual) => state = actual,
}
};
self.slot(target, CURRENT).with_mut(|slot| unsafe {
if !(*slot).as_ref().is_some_and(|w| w.will_wake(waker)) {
*slot = Some(waker.clone());
}
});
let before = self.state.fetch_xor(REGISTERING | ARMED, Ordering::AcqRel);
assert!(
before & NUDGED == 0 && before & PHASE != GRANTED,
"the waiter armed and slept beside an unread mark"
);
Park::Pending
}
}
pub(super) enum Taken {
Again,
Done(bool),
}
pub(super) fn release(node: NonNull<Waiter>, bit: usize, queue: &WaitQueue) {
let waiter = unsafe { node.as_ref() };
if bit == QUEUE_REF && waiter.is_owed() {
queue.forgive();
}
let prev = waiter.state.fetch_and(!bit, Ordering::AcqRel);
if prev & REFS == bit {
queue.pool().put(node);
}
}
pub(in crate::sync) fn release_queued(node: NonNull<Waiter>, queue: &WaitQueue) {
release(node, QUEUE_REF, queue);
}