#![cfg_attr(coverage_nightly, coverage(off))]
use std::cell::Cell;
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use std::sync::{Arc, Barrier, Mutex};
pub(crate) type HookFn = dyn Fn() + Send + Sync;
pub(crate) type HookSlot = Mutex<Option<Arc<HookFn>>>;
pub(crate) static HOOK_SERIALIZATION_MUTEX: Mutex<()> = Mutex::new(());
pub(crate) static AUTO_PRE_MUTEX: HookSlot = Mutex::new(None);
pub(crate) static AUTO_PRE_TRY_WAIT: HookSlot = Mutex::new(None);
pub(crate) static AUTO_PRE_FETCH_OR: HookSlot = Mutex::new(None);
pub(crate) static AUTO_SET_PRE_LOCK: HookSlot = Mutex::new(None);
pub(crate) static MANUAL_PRE_MUTEX: HookSlot = Mutex::new(None);
pub(crate) static MANUAL_PRE_LOAD: HookSlot = Mutex::new(None);
pub(crate) static MANUAL_PRE_FETCH_OR: HookSlot = Mutex::new(None);
thread_local! {
pub(crate) static HOOK_PARTICIPANT: Cell<bool> = const { Cell::new(false) };
}
pub(crate) fn run(slot: &HookSlot) {
if !HOOK_PARTICIPANT.with(Cell::get) {
return;
}
let hook = slot.lock().expect(crate::NEVER_POISONED).clone();
if let Some(hook) = hook {
hook();
}
}
pub(crate) fn with_hook(slot: &HookSlot, closure: Arc<HookFn>, body: impl FnOnce()) {
let guard = HOOK_SERIALIZATION_MUTEX
.lock()
.expect(crate::NEVER_POISONED);
*slot.lock().expect(crate::NEVER_POISONED) = Some(closure);
let result = catch_unwind(AssertUnwindSafe(body));
*slot.lock().expect(crate::NEVER_POISONED) = None;
drop(guard);
if let Err(payload) = result {
resume_unwind(payload);
}
}
pub(crate) struct BarrierHook {
pub(crate) entered: Arc<Barrier>,
pub(crate) proceed: Arc<Barrier>,
pub(crate) hook: Arc<HookFn>,
}
pub(crate) fn barrier_hook() -> BarrierHook {
let entered = Arc::new(Barrier::new(2));
let proceed = Arc::new(Barrier::new(2));
let hook: Arc<HookFn> = Arc::new({
let entered = Arc::clone(&entered);
let proceed = Arc::clone(&proceed);
move || {
entered.wait();
proceed.wait();
}
});
BarrierHook {
entered,
proceed,
hook,
}
}