pub mod registry;
use crate::registry::MemoryRegistryError;
use std::sync::{
Mutex,
atomic::{AtomicBool, Ordering},
};
static CANIC_EAGER_TLS: Mutex<Vec<fn()>> = Mutex::new(Vec::new());
static CANIC_EAGER_TLS_RUNNING: AtomicBool = AtomicBool::new(false);
static CANIC_EAGER_INIT: 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() {
struct RunningGuard;
impl Drop for RunningGuard {
fn drop(&mut self) {
CANIC_EAGER_TLS_RUNNING.store(false, Ordering::SeqCst);
}
}
CANIC_EAGER_TLS_RUNNING.store(true, Ordering::SeqCst);
let _running_guard = RunningGuard;
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();
}
}
pub fn run_registered_eager_init() {
let funcs = {
let mut funcs = CANIC_EAGER_INIT.lock().expect("eager init queue poisoned");
std::mem::take(&mut *funcs)
};
debug_assert!(
CANIC_EAGER_INIT
.lock()
.expect("eager init queue poisoned")
.is_empty(),
"CANIC_EAGER_INIT was modified during eager init execution"
);
for f in funcs {
f();
}
}
#[must_use]
pub fn is_eager_tls_initializing() -> bool {
CANIC_EAGER_TLS_RUNNING.load(Ordering::SeqCst)
}
#[must_use]
pub fn is_memory_bootstrap_ready() -> bool {
registry::MemoryRegistryRuntime::is_initialized()
}
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 init_eager_tls() and MemoryRegistryRuntime::init(...) first"
);
}
pub fn defer_tls_initializer(f: fn()) {
CANIC_EAGER_TLS
.lock()
.expect("eager tls queue poisoned")
.push(f);
}
pub fn defer_eager_init(f: fn()) {
CANIC_EAGER_INIT
.lock()
.expect("eager init 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();
}
}
pub struct MemoryRuntimeApi;
impl MemoryRuntimeApi {
pub fn bootstrap_registry(
crate_name: &'static str,
start: u8,
end: u8,
) -> Result<registry::MemoryRegistryInitSummary, MemoryRegistryError> {
run_registered_eager_init();
let summary = registry::MemoryRegistryRuntime::init(Some((crate_name, start, end)))?;
init_eager_tls();
Ok(summary)
}
pub fn bootstrap_registry_without_range()
-> Result<registry::MemoryRegistryInitSummary, MemoryRegistryError> {
run_registered_eager_init();
let summary = registry::MemoryRegistryRuntime::init(None)?;
init_eager_tls();
Ok(summary)
}
}
#[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 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");
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);
}
#[test]
fn run_registered_eager_init_runs_and_clears_queue() {
let _guard = TEST_LOCK.lock().expect("test lock poisoned");
COUNT.store(0, Ordering::SeqCst);
CANIC_EAGER_INIT
.lock()
.expect("eager init queue poisoned")
.push(bump);
run_registered_eager_init();
let first = COUNT.load(Ordering::SeqCst);
assert_eq!(first, 1);
run_registered_eager_init();
let second = COUNT.load(Ordering::SeqCst);
assert_eq!(second, 1);
}
}