use std::collections::HashMap;
use std::sync::Mutex;
use std::task::Waker;
#[derive(Debug, Default)]
pub(crate) struct WakerRegistry {
wakers: Mutex<HashMap<u64, Waker>>,
}
impl WakerRegistry {
#[allow(dead_code)] pub(crate) fn new() -> Self {
Self {
wakers: Mutex::new(HashMap::new()),
}
}
pub(crate) fn register_waker(&self, operation_id: u64, waker: Waker) {
self.wakers.lock().unwrap().insert(operation_id, waker);
}
#[allow(dead_code)] pub(crate) fn wake_operation(&self, operation_id: u64) -> bool {
if let Some(waker) = self.wakers.lock().unwrap().remove(&operation_id) {
waker.wake();
true
} else {
false
}
}
pub(crate) fn remove_waker(&self, operation_id: u64) -> bool {
self.wakers.lock().unwrap().remove(&operation_id).is_some()
}
#[cfg(test)]
pub(crate) fn waker_count(&self) -> usize {
self.wakers.lock().unwrap().len()
}
#[cfg(test)]
pub(crate) fn has_wakers(&self) -> bool {
!self.wakers.lock().unwrap().is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
use std::task::{RawWaker, RawWakerVTable, Waker};
fn create_test_waker() -> (Waker, Arc<Mutex<bool>>) {
let woken = Arc::new(Mutex::new(false));
let woken_clone = woken.clone();
let raw_waker = RawWaker::new(
Arc::into_raw(woken_clone) as *const (),
&RawWakerVTable::new(
|data| {
let woken = unsafe { Arc::from_raw(data as *const Mutex<bool>) };
let woken_clone = woken.clone();
std::mem::forget(woken);
RawWaker::new(
Arc::into_raw(woken_clone) as *const (),
&RawWakerVTable::new(
|_| panic!("clone should not be called"),
|data| {
let woken = unsafe { Arc::from_raw(data as *const Mutex<bool>) };
*woken.lock().unwrap() = true;
},
|data| {
let woken = unsafe { Arc::from_raw(data as *const Mutex<bool>) };
*woken.lock().unwrap() = true;
},
|data| {
let _ = unsafe { Arc::from_raw(data as *const Mutex<bool>) };
},
),
)
},
|data| {
let woken = unsafe { Arc::from_raw(data as *const Mutex<bool>) };
*woken.lock().unwrap() = true;
},
|data| {
let woken = unsafe { Arc::from_raw(data as *const Mutex<bool>) };
*woken.lock().unwrap() = true;
},
|data| {
let _ = unsafe { Arc::from_raw(data as *const Mutex<bool>) };
},
),
);
(unsafe { Waker::from_raw(raw_waker) }, woken)
}
#[test]
fn new_registry_is_empty() {
let registry = WakerRegistry::new();
assert_eq!(registry.waker_count(), 0);
assert!(!registry.has_wakers());
}
#[test]
fn register_and_wake_waker() {
let registry = WakerRegistry::new();
let (waker, woken) = create_test_waker();
registry.register_waker(1, waker);
assert_eq!(registry.waker_count(), 1);
assert!(registry.has_wakers());
let was_woken = registry.wake_operation(1);
assert!(was_woken);
assert_eq!(registry.waker_count(), 0);
assert!(!registry.has_wakers());
assert!(*woken.lock().unwrap());
}
#[test]
fn wake_nonexistent_operation() {
let registry = WakerRegistry::new();
let was_woken = registry.wake_operation(999);
assert!(!was_woken);
}
#[test]
fn remove_waker_without_waking() {
let registry = WakerRegistry::new();
let (waker, woken) = create_test_waker();
registry.register_waker(1, waker);
assert_eq!(registry.waker_count(), 1);
let was_removed = registry.remove_waker(1);
assert!(was_removed);
assert_eq!(registry.waker_count(), 0);
assert!(!*woken.lock().unwrap());
}
#[test]
fn remove_nonexistent_waker() {
let registry = WakerRegistry::new();
let was_removed = registry.remove_waker(999);
assert!(!was_removed);
}
#[test]
fn replace_existing_waker() {
let registry = WakerRegistry::new();
let (waker1, woken1) = create_test_waker();
let (waker2, woken2) = create_test_waker();
registry.register_waker(1, waker1);
registry.register_waker(1, waker2);
assert_eq!(registry.waker_count(), 1);
registry.wake_operation(1);
assert!(!*woken1.lock().unwrap());
assert!(*woken2.lock().unwrap());
}
}