canic_core/memory/runtime/
mod.rs1pub mod registry;
2
3use std::sync::{
4 Mutex,
5 atomic::{AtomicBool, Ordering},
6};
7
8static CANIC_EAGER_TLS: Mutex<Vec<fn()>> = Mutex::new(Vec::new());
25static CANIC_EAGER_TLS_RUNNING: AtomicBool = AtomicBool::new(false);
26static CANIC_EAGER_INIT: Mutex<Vec<fn()>> = Mutex::new(Vec::new());
27#[cfg(any(test, debug_assertions))]
28static TEST_BOOTSTRAP_HOOK: Mutex<Option<fn()>> = Mutex::new(None);
29
30pub fn init_eager_tls() {
44 struct RunningGuard;
49
50 impl Drop for RunningGuard {
51 fn drop(&mut self) {
52 CANIC_EAGER_TLS_RUNNING.store(false, Ordering::SeqCst);
53 }
54 }
55
56 CANIC_EAGER_TLS_RUNNING.store(true, Ordering::SeqCst);
57 let _running_guard = RunningGuard;
58
59 let funcs = {
60 let mut funcs = CANIC_EAGER_TLS.lock().expect("eager tls queue poisoned");
61 std::mem::take(&mut *funcs)
62 };
63
64 debug_assert!(
65 CANIC_EAGER_TLS
66 .lock()
67 .expect("eager tls queue poisoned")
68 .is_empty(),
69 "CANIC_EAGER_TLS was modified during init_eager_tls() execution"
70 );
71
72 for f in funcs {
73 f();
74 }
75}
76
77pub fn run_registered_eager_init() {
84 let funcs = {
85 let mut funcs = CANIC_EAGER_INIT.lock().expect("eager init queue poisoned");
86 std::mem::take(&mut *funcs)
87 };
88
89 debug_assert!(
90 CANIC_EAGER_INIT
91 .lock()
92 .expect("eager init queue poisoned")
93 .is_empty(),
94 "CANIC_EAGER_INIT was modified during eager init execution"
95 );
96
97 for f in funcs {
98 f();
99 }
100}
101
102#[must_use]
104pub fn is_eager_tls_initializing() -> bool {
105 CANIC_EAGER_TLS_RUNNING.load(Ordering::SeqCst)
106}
107
108#[must_use]
110pub fn is_memory_bootstrap_ready() -> bool {
111 registry::MemoryRegistryRuntime::validated_allocations().is_ok()
112}
113
114pub fn assert_memory_bootstrap_ready(label: &str, id: u8) {
116 if is_memory_bootstrap_ready() {
117 return;
118 }
119
120 #[cfg(any(test, debug_assertions))]
121 {
122 run_test_bootstrap_hook();
123 if is_memory_bootstrap_ready() {
124 return;
125 }
126 }
127
128 panic!(
129 "stable memory slot '{label}' (id {id}) accessed before memory bootstrap; call init_eager_tls() and MemoryRegistryRuntime::init(...) first"
130 );
131}
132
133pub fn defer_tls_initializer(f: fn()) {
139 CANIC_EAGER_TLS
140 .lock()
141 .expect("eager tls queue poisoned")
142 .push(f);
143}
144
145pub fn defer_eager_init(f: fn()) {
147 CANIC_EAGER_INIT
148 .lock()
149 .expect("eager init queue poisoned")
150 .push(f);
151}
152
153#[cfg(any(test, debug_assertions))]
156pub fn install_test_bootstrap_hook(hook: fn()) {
157 *TEST_BOOTSTRAP_HOOK
158 .lock()
159 .expect("test bootstrap hook poisoned") = Some(hook);
160}
161
162#[cfg(any(test, debug_assertions))]
164#[must_use]
165pub fn has_test_bootstrap_hook() -> bool {
166 TEST_BOOTSTRAP_HOOK
167 .lock()
168 .expect("test bootstrap hook poisoned")
169 .is_some()
170}
171
172#[cfg(any(test, debug_assertions))]
173fn run_test_bootstrap_hook() {
174 let hook = *TEST_BOOTSTRAP_HOOK
175 .lock()
176 .expect("test bootstrap hook poisoned");
177 if let Some(hook) = hook {
178 hook();
179 }
180}
181
182#[cfg(test)]
187mod tests {
188 use super::*;
189 use std::sync::{
190 Mutex,
191 atomic::{AtomicU32, Ordering},
192 };
193
194 static COUNT: AtomicU32 = AtomicU32::new(0);
195 static TEST_LOCK: Mutex<()> = Mutex::new(());
196
197 fn clear_test_queues() {
198 CANIC_EAGER_TLS
199 .lock()
200 .expect("eager tls queue poisoned")
201 .clear();
202 CANIC_EAGER_INIT
203 .lock()
204 .expect("eager init queue poisoned")
205 .clear();
206 }
207
208 fn bump() {
209 COUNT.fetch_add(1, Ordering::SeqCst);
210 }
211
212 #[test]
213 fn init_eager_tls_runs_and_clears_queue() {
214 let _guard = TEST_LOCK.lock().expect("test lock poisoned");
215 clear_test_queues();
216 COUNT.store(0, Ordering::SeqCst);
217 CANIC_EAGER_TLS
218 .lock()
219 .expect("eager tls queue poisoned")
220 .push(bump);
221 init_eager_tls();
222 let first = COUNT.load(Ordering::SeqCst);
223 assert_eq!(first, 1);
224
225 init_eager_tls();
227 let second = COUNT.load(Ordering::SeqCst);
228 assert_eq!(second, 1);
229 }
230
231 #[test]
232 fn run_registered_eager_init_runs_and_clears_queue() {
233 let _guard = TEST_LOCK.lock().expect("test lock poisoned");
234 clear_test_queues();
235 COUNT.store(0, Ordering::SeqCst);
236 CANIC_EAGER_INIT
237 .lock()
238 .expect("eager init queue poisoned")
239 .push(bump);
240 run_registered_eager_init();
241 let first = COUNT.load(Ordering::SeqCst);
242 assert_eq!(first, 1);
243
244 run_registered_eager_init();
246 let second = COUNT.load(Ordering::SeqCst);
247 assert_eq!(second, 1);
248 }
249}