use std::fmt;
use std::sync::{Mutex, MutexGuard, OnceLock};
pub const MAX_IDLE_HOOKS: usize = 16;
pub type IdleHook = fn();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum IdleHookRegistrationError {
CapacityExhausted,
}
impl fmt::Display for IdleHookRegistrationError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CapacityExhausted => write!(
formatter,
"worker idle-hook registry capacity ({MAX_IDLE_HOOKS}) is exhausted"
),
}
}
}
impl std::error::Error for IdleHookRegistrationError {}
struct HookRegistry {
hooks: Mutex<[Option<IdleHook>; MAX_IDLE_HOOKS]>,
}
impl HookRegistry {
const fn new() -> Self {
Self {
hooks: Mutex::new([None; MAX_IDLE_HOOKS]),
}
}
fn lock(&self) -> MutexGuard<'_, [Option<IdleHook>; MAX_IDLE_HOOKS]> {
self.hooks
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
fn register(&self, hook: IdleHook) -> Result<(), IdleHookRegistrationError> {
let mut hooks = self.lock();
let Some(slot) = hooks.iter_mut().find(|entry| entry.is_none()) else {
return Err(IdleHookRegistrationError::CapacityExhausted);
};
*slot = Some(hook);
Ok(())
}
fn run(&self) {
let snapshot = {
let hooks = self.lock();
*hooks
};
for hook in snapshot.into_iter().flatten() {
hook();
}
}
}
static HOOKS: OnceLock<HookRegistry> = OnceLock::new();
fn registry() -> &'static HookRegistry {
HOOKS.get_or_init(HookRegistry::new)
}
pub fn register_idle_hook(hook: IdleHook) -> Result<(), IdleHookRegistrationError> {
registry().register(hook)
}
pub fn run_idle_hooks() {
registry().run();
}
#[cfg(test)]
mod tests;