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