#![allow(unsafe_code)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum ForkGuard {
Generation(u64),
ProcessId(u32),
}
impl ForkGuard {
pub(crate) fn capture() -> Self {
generation().map_or_else(|| Self::ProcessId(current_process_id()), Self::Generation)
}
pub(crate) fn unchanged(self) -> bool {
match self {
Self::Generation(seen) => generation() == Some(seen),
Self::ProcessId(seen) => current_process_id() == seen,
}
}
}
fn current_process_id() -> u32 {
std::process::id()
}
#[cfg(unix)]
fn generation() -> Option<u64> {
use core::sync::atomic::{AtomicU64, Ordering};
use std::sync::OnceLock;
static FORK_GENERATION: AtomicU64 = AtomicU64::new(0);
extern "C" fn bump_generation_in_child() {
FORK_GENERATION.fetch_add(1, Ordering::SeqCst);
}
static INSTALLED: OnceLock<bool> = OnceLock::new();
let installed = *INSTALLED.get_or_init(|| {
unsafe { libc::pthread_atfork(None, None, Some(bump_generation_in_child)) == 0 }
});
installed.then(|| FORK_GENERATION.load(Ordering::Acquire))
}
#[cfg(not(unix))]
fn generation() -> Option<u64> {
None
}