use std::sync::Mutex;
static CANIC_EAGER_TLS: Mutex<Vec<fn()>> = Mutex::new(Vec::new());
#[cfg(any(test, debug_assertions))]
static TEST_BOOTSTRAP_HOOK: Mutex<Option<fn()>> = Mutex::new(None);
pub fn init_eager_tls() {
let funcs = {
let mut funcs = CANIC_EAGER_TLS.lock().expect("eager tls queue poisoned");
std::mem::take(&mut *funcs)
};
debug_assert!(
CANIC_EAGER_TLS
.lock()
.expect("eager tls queue poisoned")
.is_empty(),
"CANIC_EAGER_TLS was modified during init_eager_tls() execution"
);
for f in funcs {
f();
}
}
#[must_use]
pub fn is_memory_bootstrap_ready() -> bool {
ic_memory::runtime::is_default_memory_manager_bootstrapped()
}
pub fn assert_memory_bootstrap_ready(label: &str, id: u8) {
if is_memory_bootstrap_ready() {
return;
}
#[cfg(any(test, debug_assertions))]
{
run_test_bootstrap_hook();
if is_memory_bootstrap_ready() {
return;
}
}
panic!(
"stable memory slot '{label}' (id {id}) accessed before memory bootstrap; call ic_memory::bootstrap_default_memory_manager_with_policy(...) first"
);
}
pub fn defer_tls_initializer(f: fn()) {
CANIC_EAGER_TLS
.lock()
.expect("eager tls queue poisoned")
.push(f);
}
#[cfg(any(test, debug_assertions))]
pub fn install_test_bootstrap_hook(hook: fn()) {
*TEST_BOOTSTRAP_HOOK
.lock()
.expect("test bootstrap hook poisoned") = Some(hook);
}
#[cfg(any(test, debug_assertions))]
#[must_use]
pub fn has_test_bootstrap_hook() -> bool {
TEST_BOOTSTRAP_HOOK
.lock()
.expect("test bootstrap hook poisoned")
.is_some()
}
#[cfg(any(test, debug_assertions))]
fn run_test_bootstrap_hook() {
let hook = *TEST_BOOTSTRAP_HOOK
.lock()
.expect("test bootstrap hook poisoned");
if let Some(hook) = hook {
hook();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{
Mutex,
atomic::{AtomicU32, Ordering},
};
static COUNT: AtomicU32 = AtomicU32::new(0);
static TEST_LOCK: Mutex<()> = Mutex::new(());
fn clear_test_queues() {
CANIC_EAGER_TLS
.lock()
.expect("eager tls queue poisoned")
.clear();
}
fn bump() {
COUNT.fetch_add(1, Ordering::SeqCst);
}
#[test]
fn init_eager_tls_runs_and_clears_queue() {
let _guard = TEST_LOCK.lock().expect("test lock poisoned");
clear_test_queues();
COUNT.store(0, Ordering::SeqCst);
CANIC_EAGER_TLS
.lock()
.expect("eager tls queue poisoned")
.push(bump);
init_eager_tls();
let first = COUNT.load(Ordering::SeqCst);
assert_eq!(first, 1);
init_eager_tls();
let second = COUNT.load(Ordering::SeqCst);
assert_eq!(second, 1);
}
}