use std::{
collections::HashMap,
sync::{Arc, Condvar, Mutex, OnceLock},
};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum HookPoint {
TryWriteOrBeforeAcquire,
WriteGuardBeforeDrop,
ReadGuardAfterRelease,
DrainAfterWriteLockRelease,
DrainBeforeCallbacks,
WriteGuardAfterSettingDropping,
ReadGuardAfterSettingDropping,
TryWriteOrWhileDropping,
}
type HookFn = Arc<dyn Fn() + Send + Sync + 'static>;
fn registry() -> &'static Mutex<HashMap<HookPoint, HookFn>> {
static REGISTRY: OnceLock<Mutex<HashMap<HookPoint, HookFn>>> = OnceLock::new();
REGISTRY.get_or_init(Default::default)
}
pub(crate) fn run(point: HookPoint) {
let f = registry().lock().unwrap().get(&point).cloned();
if let Some(f) = f {
f();
}
}
pub fn set(point: HookPoint, f: impl Fn() + Send + Sync + 'static) {
registry().lock().unwrap().insert(point, Arc::new(f));
}
pub fn clear(point: HookPoint) {
registry().lock().unwrap().remove(&point);
}
pub fn clear_all() {
registry().lock().unwrap().clear();
}
pub struct TestGuard(#[allow(dead_code)] std::sync::MutexGuard<'static, ()>);
impl TestGuard {
pub fn acquire() -> Self {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
let guard = LOCK
.get_or_init(Default::default)
.lock()
.unwrap_or_else(|e| e.into_inner());
clear_all();
Self(guard)
}
}
impl Drop for TestGuard {
fn drop(&mut self) {
clear_all();
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
enum GateState {
#[default]
Idle,
Arrived,
Open,
}
#[derive(Debug)]
pub struct Gate {
state: Mutex<GateState>,
cv: Condvar,
}
impl Gate {
pub fn new() -> Arc<Self> {
Arc::new(Self {
state: Mutex::new(GateState::Idle),
cv: Condvar::new(),
})
}
pub fn wait(&self) {
let mut s = self.state.lock().unwrap();
*s = GateState::Arrived;
self.cv.notify_all();
while *s == GateState::Arrived {
s = self.cv.wait(s).unwrap();
}
}
pub fn wait_for_arrival(&self) {
let mut s = self.state.lock().unwrap();
while *s != GateState::Arrived {
s = self.cv.wait(s).unwrap();
}
}
pub fn open(&self) {
let mut s = self.state.lock().unwrap();
*s = GateState::Open;
self.cv.notify_all();
}
pub fn signal(&self) {
let mut s = self.state.lock().unwrap();
*s = GateState::Arrived;
self.cv.notify_all();
}
#[allow(unused)]
pub fn reset(&self) {
*self.state.lock().unwrap() = GateState::Idle;
}
}