use std::collections::VecDeque;
use std::marker::{PhantomData, PhantomPinned};
use std::pin::Pin;
use std::ptr;
use std::sync::atomic::{self, AtomicBool, AtomicUsize};
use std::sync::{Arc, Mutex};
use std::task::{RawWaker, RawWakerVTable, Waker};
use pin_project::{pin_project, pinned_drop};
use crate::TaskRef;
#[derive(Debug)]
#[pin_project(PinnedDrop)]
pub(crate) struct WakeSignal {
task_ref: TaskRef,
awakened_queue: Arc<Mutex<VecDeque<TaskRef>>>,
probe_embedded_wake_signals: Arc<AtomicBool>,
waker_count: AtomicUsize,
awakened: AtomicBool,
parent_waker: Waker,
_single_threaded: PhantomData<*const ()>,
_requires_pin: PhantomPinned,
}
impl WakeSignal {
pub(crate) fn new(
awakened_queue: Arc<Mutex<VecDeque<TaskRef>>>,
probe_embedded_wake_signals: Arc<AtomicBool>,
parent_waker: Waker,
task_ref: TaskRef,
) -> Self {
Self {
task_ref,
awakened_queue,
probe_embedded_wake_signals,
waker_count: AtomicUsize::new(0),
awakened: AtomicBool::new(false),
parent_waker,
_single_threaded: PhantomData,
_requires_pin: PhantomPinned,
}
}
#[cfg(test)]
pub(crate) fn fake() -> Self {
let fake_task_ref = unsafe { TaskRef::fake() };
Self::new(
Arc::new(Mutex::new(VecDeque::new())),
Arc::new(AtomicBool::new(false)),
Waker::noop().clone(),
fake_task_ref,
)
}
pub(crate) fn consume_awakened(&self) -> bool {
if self.awakened.swap(false, atomic::Ordering::Relaxed) {
atomic::fence(atomic::Ordering::Acquire);
true
} else {
false
}
}
#[cfg_attr(test, mutants::skip)] pub(crate) fn is_inert(&self) -> bool {
self.waker_count.load(atomic::Ordering::Acquire) == 0
}
pub(crate) unsafe fn waker(self: Pin<&Self>) -> Waker {
self.waker_count.fetch_add(1, atomic::Ordering::Relaxed);
let signal_ptr: *const Self = ptr::from_ref(self.get_ref());
unsafe { Waker::from_raw(RawWaker::new(signal_ptr.cast(), &WAKER_VTABLE)) }
}
fn wake(&self) {
if let Ok(mut awakened_set) = self.awakened_queue.try_lock() {
if awakened_set.len() < awakened_set.capacity() {
awakened_set.push_back(self.task_ref);
drop(awakened_set);
self.parent_waker.wake_by_ref();
return;
}
}
self.awakened.store(true, atomic::Ordering::Release);
self.probe_embedded_wake_signals.store(true, atomic::Ordering::Release);
self.parent_waker.wake_by_ref();
}
}
#[pinned_drop]
impl PinnedDrop for WakeSignal {
#[cfg_attr(test, mutants::skip)] fn drop(self: Pin<&mut Self>) {
debug_assert!(self.is_inert());
}
}
static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(waker_clone_waker, waker_wake, waker_wake_by_ref, waker_drop_waker);
fn waker_clone_waker(ptr: *const ()) -> RawWaker {
let signal = resurrect_signal_ref(ptr);
signal.waker_count.fetch_add(1, atomic::Ordering::Relaxed);
RawWaker::new(ptr, &WAKER_VTABLE)
}
#[cfg_attr(test, mutants::skip)] fn waker_wake(ptr: *const ()) {
let signal = resurrect_signal_ref(ptr);
signal.wake();
signal.waker_count.fetch_sub(1, atomic::Ordering::Release);
}
#[cfg_attr(test, mutants::skip)] fn waker_wake_by_ref(ptr: *const ()) {
let signal = resurrect_signal_ref(ptr);
signal.wake();
}
#[cfg_attr(test, mutants::skip)] fn waker_drop_waker(ptr: *const ()) {
let signal = resurrect_signal_ref(ptr);
signal.waker_count.fetch_sub(1, atomic::Ordering::Release);
}
#[cfg_attr(coverage_nightly, coverage(off))] fn resurrect_signal_ref(ptr: *const ()) -> &'static WakeSignal {
let wake_signal = unsafe { ptr.cast::<WakeSignal>().as_ref() };
let Some(wake_signal) = wake_signal else {
unreachable!("waker has a null pointer for its inner state - impossible")
};
wake_signal
}
#[cfg(test)]
mod tests {
use std::pin::pin;
use super::*;
use crate::testing::TestWaker;
#[test]
fn never_used() {
let fake_task_ref = unsafe { TaskRef::fake() };
let awakened_queue = Arc::new(Mutex::new(VecDeque::with_capacity(10)));
let probe_embedded_wake_signals = Arc::new(AtomicBool::new(false));
let signal = pin!(WakeSignal::new(
Arc::clone(&awakened_queue),
Arc::clone(&probe_embedded_wake_signals),
Waker::noop().clone(),
fake_task_ref
));
assert!(signal.is_inert());
}
#[test]
fn awaken_via_embedded_signal() {
let fake_task_ref = unsafe { TaskRef::fake() };
let awakened_queue = Arc::new(Mutex::new(VecDeque::with_capacity(10)));
let probe_embedded_wake_signals = Arc::new(AtomicBool::new(false));
let parent_waker = Arc::new(TestWaker::new());
let _awakened_set_lock_guard = awakened_queue.lock().unwrap();
let signal = pin!(WakeSignal::new(
Arc::clone(&awakened_queue),
Arc::clone(&probe_embedded_wake_signals),
Arc::clone(&parent_waker).into(),
fake_task_ref
));
let signal = signal.as_ref();
let waker = unsafe { signal.waker() };
assert!(!signal.consume_awakened());
assert!(!parent_waker.awakened.load(atomic::Ordering::Relaxed));
waker.wake_by_ref();
assert!(probe_embedded_wake_signals.load(atomic::Ordering::Relaxed));
assert!(signal.consume_awakened());
assert!(parent_waker.awakened.load(atomic::Ordering::Relaxed));
assert!(!signal.consume_awakened());
}
#[test]
fn awaken_via_awakened_set() {
let fake_task_ref = unsafe { TaskRef::fake() };
let awakened_queue = Arc::new(Mutex::new(VecDeque::with_capacity(10)));
let probe_embedded_wake_signals = Arc::new(AtomicBool::new(false));
let parent_waker = Arc::new(TestWaker::new());
let signal = pin!(WakeSignal::new(
Arc::clone(&awakened_queue),
Arc::clone(&probe_embedded_wake_signals),
Arc::clone(&parent_waker).into(),
fake_task_ref
));
let signal = signal.as_ref();
let waker = unsafe { signal.waker() };
assert!(!signal.consume_awakened());
assert!(!parent_waker.awakened.load(atomic::Ordering::Relaxed));
waker.wake_by_ref();
assert!(!probe_embedded_wake_signals.load(atomic::Ordering::Relaxed));
assert!(!signal.consume_awakened());
assert!(!awakened_queue.lock().unwrap().is_empty());
assert!(parent_waker.awakened.load(atomic::Ordering::Relaxed));
}
#[test]
fn awaken_via_full_awakened_set() {
let fake_task_ref = unsafe { TaskRef::fake() };
let awakened_queue = Arc::new(Mutex::new(VecDeque::new()));
let probe_embedded_wake_signals = Arc::new(AtomicBool::new(false));
let parent_waker = Arc::new(TestWaker::new());
let signal = pin!(WakeSignal::new(
Arc::clone(&awakened_queue),
Arc::clone(&probe_embedded_wake_signals),
Arc::clone(&parent_waker).into(),
fake_task_ref
));
let signal = signal.as_ref();
let waker = unsafe { signal.waker() };
assert!(!signal.consume_awakened());
assert!(!parent_waker.awakened.load(atomic::Ordering::Relaxed));
waker.wake_by_ref();
assert!(probe_embedded_wake_signals.load(atomic::Ordering::Relaxed));
assert!(signal.consume_awakened());
assert!(parent_waker.awakened.load(atomic::Ordering::Relaxed));
}
#[test]
fn is_inert_when_expected() {
let fake_task_ref = unsafe { TaskRef::fake() };
let awakened_queue = Arc::new(Mutex::new(VecDeque::with_capacity(10)));
let probe_embedded_wake_signals = Arc::new(AtomicBool::new(false));
let signal = pin!(WakeSignal::new(
Arc::clone(&awakened_queue),
Arc::clone(&probe_embedded_wake_signals),
Waker::noop().clone(),
fake_task_ref
));
let signal = signal.as_ref();
let waker = unsafe { signal.waker() };
assert!(!signal.is_inert());
let waker_clone = waker.clone();
assert!(!signal.is_inert());
assert_eq!(signal.waker_count.load(atomic::Ordering::Relaxed), 2);
drop(waker);
drop(waker_clone);
assert_eq!(signal.waker_count.load(atomic::Ordering::Relaxed), 0);
assert!(signal.is_inert());
let waker = unsafe { signal.waker() };
assert!(!signal.is_inert());
let waker_clone = waker.clone();
assert!(!signal.is_inert());
assert_eq!(signal.waker_count.load(atomic::Ordering::Relaxed), 2);
drop(waker);
drop(waker_clone);
assert_eq!(signal.waker_count.load(atomic::Ordering::Relaxed), 0);
assert!(signal.is_inert());
}
#[test]
fn consuming_waker_wakes_signal() {
let fake_task_ref = unsafe { TaskRef::fake() };
let awakened_queue = Arc::new(Mutex::new(VecDeque::with_capacity(1)));
let probe_embedded_wake_signals = Arc::new(AtomicBool::new(false));
let signal = pin!(WakeSignal::new(
Arc::clone(&awakened_queue),
Arc::clone(&probe_embedded_wake_signals),
Waker::noop().clone(),
fake_task_ref
));
let signal = signal.as_ref();
let waker = unsafe { signal.waker() };
waker.wake();
assert_eq!(signal.waker_count.load(atomic::Ordering::Relaxed), 0);
assert!(!awakened_queue.lock().unwrap().is_empty());
assert!(signal.is_inert());
}
}