#![allow(unsafe_code)]
use core::mem;
use core::ptr::{self, NonNull};
use core::task::Waker;
use super::node::{GRANTED, NUDGED, Taken, Touch, Waiter, release_queued};
use crate::sync::internal::{Ordering, during_unwind};
use crate::sync::queue::WaitQueue;
pub(in crate::sync) struct WakeList<'a> {
head: *mut Waiter,
tail: *mut Waiter,
queue: &'a WaitQueue,
}
impl<'a> WakeList<'a> {
pub(in crate::sync) fn new(queue: &'a WaitQueue) -> Self {
Self {
head: ptr::null_mut(),
tail: ptr::null_mut(),
queue,
}
}
pub(in crate::sync) fn grant(&mut self, node: NonNull<Waiter>) -> bool {
let touch = unsafe { node.as_ref() }.touch(GRANTED, false);
if let Touch::Claimed = touch {
self.push(node);
}
release_queued(node, self.queue);
!matches!(touch, Touch::Skipped)
}
pub(in crate::sync) fn nudge(&mut self, node: NonNull<Waiter>) -> bool {
match unsafe { node.as_ref() }.touch(NUDGED, true) {
Touch::Claimed => {
self.push(node);
true
}
Touch::Marked | Touch::Pending => true,
Touch::Skipped => false,
}
}
fn push(&mut self, node: NonNull<Waiter>) {
unsafe { node.as_ref() }
.wake_next
.store(ptr::null_mut(), Ordering::Relaxed);
match NonNull::new(self.tail) {
Some(tail) => unsafe { tail.as_ref() }
.wake_next
.store(node.as_ptr(), Ordering::Relaxed),
None => self.head = node.as_ptr(),
}
self.tail = node.as_ptr();
}
fn pop(&mut self) -> Option<Option<Waker>> {
let node = NonNull::new(self.head)?;
let waiter = unsafe { node.as_ref() };
let next = waiter.wake_next.load(Ordering::Relaxed);
let (waker, taken) = waiter.take_claimed();
match taken {
Taken::Again => {}
Taken::Done(last) => {
self.head = next;
if self.head.is_null() {
self.tail = ptr::null_mut();
}
if last {
self.queue.pool().put(node);
}
}
}
Some(waker)
}
}
impl Drop for WakeList<'_> {
fn drop(&mut self) {
while let Some(waker) = self.pop() {
if let Some(waker) = waker {
let rest = WakeRest(self);
waker.wake();
mem::forget(rest);
}
}
}
}
struct WakeRest<'b, 'a>(&'b mut WakeList<'a>);
impl Drop for WakeRest<'_, '_> {
fn drop(&mut self) {
while let Some(waker) = self.0.pop() {
if let Some(waker) = waker {
during_unwind(|| waker.wake());
}
}
}
}