use std::ffi::c_void;
use std::sync::OnceLock;
static SYSTEM_THREAD_ATEXIT: OnceLock<Option<ThreadAtexitFn>> = OnceLock::new();
static HOT_RELOADING_ENABLED: OnceLock<bool> = OnceLock::new();
pub fn default_set_hot_reload() {
if cfg!(debug_assertions) {
enable_hot_reload()
} else {
disable_hot_reload()
}
}
#[macro_export]
macro_rules! register_hot_reload_workaround {
() => {
#[unsafe(no_mangle)]
#[doc(hidden)]
pub unsafe extern "C" fn __cxa_thread_atexit_impl(
func: *mut ::std::ffi::c_void,
obj: *mut ::std::ffi::c_void,
dso_symbol: *mut ::std::ffi::c_void,
) {
$crate::linux_reload_workaround::thread_atexit(func, obj, dso_symbol);
}
};
}
type ThreadAtexitFn = unsafe extern "C" fn(*mut c_void, *mut c_void, *mut c_void);
pub fn system_thread_atexit() -> &'static Option<ThreadAtexitFn> {
SYSTEM_THREAD_ATEXIT.get_or_init(|| unsafe {
let name = c"__cxa_thread_atexit_impl".as_ptr();
std::mem::transmute(libc::dlsym(libc::RTLD_NEXT, name))
})
}
pub fn is_hot_reload_enabled() -> bool {
HOT_RELOADING_ENABLED.get().copied().unwrap_or(false)
}
pub unsafe fn thread_atexit(func: *mut c_void, obj: *mut c_void, dso_symbol: *mut c_void) {
if is_hot_reload_enabled() {
} else if let Some(system_thread_atexit) = *system_thread_atexit() {
unsafe { system_thread_atexit(func, obj, dso_symbol) };
} else {
}
}
fn enable_hot_reload() {
HOT_RELOADING_ENABLED
.set(true)
.expect("hot reloading should only be set once")
}
fn disable_hot_reload() {
_ = HOT_RELOADING_ENABLED.set(false)
}
#[cfg(test)] #[cfg_attr(published_docs, doc(cfg(test)))]
mod tests {
use super::*;
#[test]
fn early_read_does_not_block_enable() {
let early = is_hot_reload_enabled();
assert!(!early, "default must be false before any enable/disable");
default_set_hot_reload();
assert_eq!(is_hot_reload_enabled(), cfg!(debug_assertions));
}
}