canic_core/memory/runtime/
mod.rs1use std::sync::Mutex;
2
3static CANIC_EAGER_TLS: Mutex<Vec<fn()>> = Mutex::new(Vec::new());
20#[cfg(any(test, debug_assertions))]
21static TEST_BOOTSTRAP_HOOK: Mutex<Option<fn()>> = Mutex::new(None);
22
23pub fn init_eager_tls() {
37 let funcs = {
38 let mut funcs = CANIC_EAGER_TLS.lock().expect("eager tls queue poisoned");
39 std::mem::take(&mut *funcs)
40 };
41
42 debug_assert!(
43 CANIC_EAGER_TLS
44 .lock()
45 .expect("eager tls queue poisoned")
46 .is_empty(),
47 "CANIC_EAGER_TLS was modified during init_eager_tls() execution"
48 );
49
50 for f in funcs {
51 f();
52 }
53}
54
55#[must_use]
57pub fn is_memory_bootstrap_ready() -> bool {
58 ic_memory::runtime::is_default_memory_manager_bootstrapped()
59}
60
61pub fn assert_memory_bootstrap_ready(label: &str, id: u8) {
63 if is_memory_bootstrap_ready() {
64 return;
65 }
66
67 #[cfg(any(test, debug_assertions))]
68 {
69 run_test_bootstrap_hook();
70 if is_memory_bootstrap_ready() {
71 return;
72 }
73 }
74
75 panic!(
76 "stable memory slot '{label}' (id {id}) accessed before memory bootstrap; call ic_memory::bootstrap_default_memory_manager_with_policy(...) first"
77 );
78}
79
80pub fn defer_tls_initializer(f: fn()) {
86 CANIC_EAGER_TLS
87 .lock()
88 .expect("eager tls queue poisoned")
89 .push(f);
90}
91
92#[cfg(any(test, debug_assertions))]
95pub fn install_test_bootstrap_hook(hook: fn()) {
96 *TEST_BOOTSTRAP_HOOK
97 .lock()
98 .expect("test bootstrap hook poisoned") = Some(hook);
99}
100
101#[cfg(any(test, debug_assertions))]
103#[must_use]
104pub fn has_test_bootstrap_hook() -> bool {
105 TEST_BOOTSTRAP_HOOK
106 .lock()
107 .expect("test bootstrap hook poisoned")
108 .is_some()
109}
110
111#[cfg(any(test, debug_assertions))]
112fn run_test_bootstrap_hook() {
113 let hook = *TEST_BOOTSTRAP_HOOK
114 .lock()
115 .expect("test bootstrap hook poisoned");
116 if let Some(hook) = hook {
117 hook();
118 }
119}
120
121#[cfg(test)]
126mod tests {
127 use super::*;
128 use std::sync::{
129 Mutex,
130 atomic::{AtomicU32, Ordering},
131 };
132
133 static COUNT: AtomicU32 = AtomicU32::new(0);
134 static TEST_LOCK: Mutex<()> = Mutex::new(());
135
136 fn clear_test_queues() {
137 CANIC_EAGER_TLS
138 .lock()
139 .expect("eager tls queue poisoned")
140 .clear();
141 }
142
143 fn bump() {
144 COUNT.fetch_add(1, Ordering::SeqCst);
145 }
146
147 #[test]
148 fn init_eager_tls_runs_and_clears_queue() {
149 let _guard = TEST_LOCK.lock().expect("test lock poisoned");
150 clear_test_queues();
151 COUNT.store(0, Ordering::SeqCst);
152 CANIC_EAGER_TLS
153 .lock()
154 .expect("eager tls queue poisoned")
155 .push(bump);
156 init_eager_tls();
157 let first = COUNT.load(Ordering::SeqCst);
158 assert_eq!(first, 1);
159
160 init_eager_tls();
162 let second = COUNT.load(Ordering::SeqCst);
163 assert_eq!(second, 1);
164 }
165}