const MAX_ATFORK: usize = 64;
#[derive(Copy, Clone)]
struct atfork_funcs {
prepare: Option<unsafe extern "C" fn()>,
parent: Option<unsafe extern "C" fn()>,
child: Option<unsafe extern "C" fn()>,
}
const EMPTY: atfork_funcs = atfork_funcs { prepare: None, parent: None, child: None };
static mut ATFORK_FUNCS: [atfork_funcs; MAX_ATFORK] = [EMPTY; MAX_ATFORK];
static ATFORK_NFUNCS: AtomicUsize = AtomicUsize::new(0);
static ATFORK_LOCK: AtomicI32 = AtomicI32::new(0);
#[inline]
unsafe fn atfork_lock() {
while ATFORK_LOCK
.compare_exchange_weak(0, 1, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
}
#[inline]
unsafe fn atfork_unlock() {
ATFORK_LOCK.store(0, Ordering::Release);
}
#[no_mangle]
pub unsafe extern "C" fn __fork_handler(who: c_int) {
let n = ATFORK_NFUNCS.load(Ordering::Relaxed);
if n == 0 {
return;
}
let base = core::ptr::addr_of!(ATFORK_FUNCS).cast::<atfork_funcs>();
if who < 0 {
atfork_lock();
let n = ATFORK_NFUNCS.load(Ordering::Relaxed);
let mut i = n;
while i > 0 {
i -= 1;
let entry = &*base.add(i);
if let Some(f) = entry.prepare {
f();
}
}
} else {
let n = ATFORK_NFUNCS.load(Ordering::Relaxed);
for i in 0..n {
let entry = &*base.add(i);
if who == 0 {
if let Some(f) = entry.parent {
f();
}
} else if let Some(f) = entry.child {
f();
}
}
atfork_unlock();
}
}
#[no_mangle]
pub unsafe extern "C" fn pthread_atfork(
prepare: Option<unsafe extern "C" fn()>,
parent: Option<unsafe extern "C" fn()>,
child: Option<unsafe extern "C" fn()>,
) -> c_int {
atfork_lock();
let n = ATFORK_NFUNCS.load(Ordering::Relaxed);
if n >= MAX_ATFORK {
atfork_unlock();
return ENOMEM;
}
let dst = core::ptr::addr_of_mut!(ATFORK_FUNCS).cast::<atfork_funcs>().add(n);
*dst = atfork_funcs { prepare, parent, child };
ATFORK_NFUNCS.store(n + 1, Ordering::Relaxed);
atfork_unlock();
0
}