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());
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 {
is_eager_tls_initializing() || registry::MemoryRegistryRuntime::is_initialized()
}
pub fn assert_memory_bootstrap_ready(label: &str, id: u8) {
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);
}
pub struct MemoryRuntimeApi;
impl MemoryRuntimeApi {
pub fn bootstrap_registry(
crate_name: &'static str,
start: u8,
end: u8,
) -> Result<registry::MemoryRegistryInitSummary, MemoryRegistryError> {
init_eager_tls();
run_registered_eager_init();
registry::MemoryRegistryRuntime::init(Some((crate_name, start, end)))
}
pub fn bootstrap_registry_without_range()
-> Result<registry::MemoryRegistryInitSummary, MemoryRegistryError> {
init_eager_tls();
run_registered_eager_init();
registry::MemoryRegistryRuntime::init(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU32, Ordering};
static COUNT: AtomicU32 = AtomicU32::new(0);
fn bump() {
COUNT.fetch_add(1, Ordering::SeqCst);
}
#[test]
fn init_eager_tls_runs_and_clears_queue() {
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() {
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);
}
}