use std::backtrace::Backtrace;
use std::sync::atomic::AtomicU64;
use std::sync::{Arc, Mutex, atomic};
use std::task::{RawWaker, RawWakerVTable, Waker};
use foldhash::{HashMap, HashMapExt};
use crate::ERR_POISONED_LOCK;
static NEXT_KEY: AtomicU64 = AtomicU64::new(0);
#[derive(Debug)]
pub(crate) struct DiagnosticWaker {
inner: Waker,
registry: Arc<DiagnosticWakerRegistry>,
key: Option<u64>,
}
impl DiagnosticWaker {
pub(crate) fn with_inner_and_registry(inner: Waker, registry: Arc<DiagnosticWakerRegistry>) -> Waker {
let instance = Self {
inner,
registry,
key: None,
};
let ptr = Box::into_raw(Box::new(instance));
unsafe { Waker::from_raw(RawWaker::new(ptr.cast(), &WAKER_VTABLE)) }
}
fn clone(&self) -> Self {
let inner_clone = self.inner.clone();
let registry = Arc::clone(&self.registry);
let key = NEXT_KEY.fetch_add(1, atomic::Ordering::Relaxed);
registry
.backtraces
.lock()
.expect(ERR_POISONED_LOCK)
.insert(key, Backtrace::capture());
Self {
inner: inner_clone,
registry,
key: Some(key),
}
}
}
impl Drop for DiagnosticWaker {
fn drop(&mut self) {
let Some(key) = self.key else {
return;
};
let mut family_backtraces = self.registry.backtraces.lock().expect(ERR_POISONED_LOCK);
assert!(family_backtraces.remove(&key).is_some());
}
}
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 waker = unwrap_diagnostic_waker(ptr);
let clone = Box::into_raw(Box::new(waker.clone()));
RawWaker::new(clone.cast(), &WAKER_VTABLE)
}
#[cfg_attr(test, mutants::skip)] fn waker_wake(ptr: *const ()) {
let waker = unwrap_diagnostic_waker(ptr);
waker.inner.wake_by_ref();
drop(unsafe { Box::from_raw(ptr.cast_mut().cast::<DiagnosticWaker>()) });
}
#[cfg_attr(test, mutants::skip)] fn waker_wake_by_ref(ptr: *const ()) {
let waker = unwrap_diagnostic_waker(ptr);
waker.inner.wake_by_ref();
}
#[cfg_attr(test, mutants::skip)] fn waker_drop_waker(ptr: *const ()) {
drop(unsafe { Box::from_raw(ptr.cast_mut().cast::<DiagnosticWaker>()) });
}
#[cfg_attr(coverage_nightly, coverage(off))] fn unwrap_diagnostic_waker(ptr: *const ()) -> &'static DiagnosticWaker {
let waker = unsafe { ptr.cast::<DiagnosticWaker>().as_ref() };
let Some(waker) = waker else {
unreachable!("waker has a null pointer for its inner state - impossible")
};
waker
}
#[derive(Debug)]
pub(crate) struct DiagnosticWakerRegistry {
backtraces: Mutex<HashMap<u64, Backtrace>>,
}
impl DiagnosticWakerRegistry {
pub(crate) fn new() -> Self {
Self {
backtraces: Mutex::new(HashMap::new()),
}
}
pub(crate) fn inspect_backtraces(&self, mut f: impl FnMut(&Backtrace)) {
let backtraces = self.backtraces.lock().expect(ERR_POISONED_LOCK);
for bt in backtraces.values() {
f(bt);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[expect(clippy::redundant_clone, reason = "intentional - testing cloning logic")]
fn smoke_test() {
let registry = Arc::new(DiagnosticWakerRegistry::new());
let inner = Waker::noop().clone();
let waker = DiagnosticWaker::with_inner_and_registry(inner, Arc::clone(®istry));
assert!(registry.backtraces.lock().unwrap().is_empty());
let waker_clone = waker.clone();
assert_eq!(registry.backtraces.lock().unwrap().len(), 1);
waker_clone.wake();
assert_eq!(registry.backtraces.lock().unwrap().len(), 0);
let waker_clone = waker.clone();
drop(waker);
let _waker_clone_clone = waker_clone.clone();
assert_eq!(registry.backtraces.lock().unwrap().len(), 2);
let mut inspect_count: usize = 0;
registry.inspect_backtraces(|_| {
inspect_count += 1;
});
assert_eq!(inspect_count, 2);
}
}