canic_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
28pub fn init_eager_tls() {
42 struct RunningGuard;
47
48 impl Drop for RunningGuard {
49 fn drop(&mut self) {
50 CANIC_EAGER_TLS_RUNNING.store(false, Ordering::SeqCst);
51 }
52 }
53
54 CANIC_EAGER_TLS_RUNNING.store(true, Ordering::SeqCst);
55 let _running_guard = RunningGuard;
56
57 let funcs = {
58 let mut funcs = CANIC_EAGER_TLS.lock().expect("eager tls queue poisoned");
59 std::mem::take(&mut *funcs)
60 };
61
62 debug_assert!(
63 CANIC_EAGER_TLS
64 .lock()
65 .expect("eager tls queue poisoned")
66 .is_empty(),
67 "CANIC_EAGER_TLS was modified during init_eager_tls() execution"
68 );
69
70 for f in funcs {
71 f();
72 }
73}
74
75pub fn run_registered_eager_init() {
81 let funcs = {
82 let mut funcs = CANIC_EAGER_INIT.lock().expect("eager init queue poisoned");
83 std::mem::take(&mut *funcs)
84 };
85
86 debug_assert!(
87 CANIC_EAGER_INIT
88 .lock()
89 .expect("eager init queue poisoned")
90 .is_empty(),
91 "CANIC_EAGER_INIT was modified during eager init execution"
92 );
93
94 for f in funcs {
95 f();
96 }
97}
98
99#[must_use]
100pub fn is_eager_tls_initializing() -> bool {
101 CANIC_EAGER_TLS_RUNNING.load(Ordering::SeqCst)
102}
103
104#[must_use]
105pub fn is_memory_bootstrap_ready() -> bool {
106 is_eager_tls_initializing() || registry::MemoryRegistryRuntime::is_initialized()
107}
108
109pub fn assert_memory_bootstrap_ready(label: &str, id: u8) {
110 if is_memory_bootstrap_ready() {
111 return;
112 }
113
114 panic!(
115 "stable memory slot '{label}' (id {id}) accessed before memory bootstrap; call init_eager_tls() and MemoryRegistryRuntime::init(...) first"
116 );
117}
118
119pub fn defer_tls_initializer(f: fn()) {
125 CANIC_EAGER_TLS
126 .lock()
127 .expect("eager tls queue poisoned")
128 .push(f);
129}
130
131pub fn defer_eager_init(f: fn()) {
133 CANIC_EAGER_INIT
134 .lock()
135 .expect("eager init queue poisoned")
136 .push(f);
137}
138
139#[cfg(test)]
144mod tests {
145 use super::*;
146 use std::sync::atomic::{AtomicU32, Ordering};
147
148 static COUNT: AtomicU32 = AtomicU32::new(0);
149
150 fn bump() {
151 COUNT.fetch_add(1, Ordering::SeqCst);
152 }
153
154 #[test]
155 fn init_eager_tls_runs_and_clears_queue() {
156 COUNT.store(0, Ordering::SeqCst);
157 CANIC_EAGER_TLS
158 .lock()
159 .expect("eager tls queue poisoned")
160 .push(bump);
161 init_eager_tls();
162 let first = COUNT.load(Ordering::SeqCst);
163 assert_eq!(first, 1);
164
165 init_eager_tls();
167 let second = COUNT.load(Ordering::SeqCst);
168 assert_eq!(second, 1);
169 }
170
171 #[test]
172 fn run_registered_eager_init_runs_and_clears_queue() {
173 COUNT.store(0, Ordering::SeqCst);
174 CANIC_EAGER_INIT
175 .lock()
176 .expect("eager init queue poisoned")
177 .push(bump);
178 run_registered_eager_init();
179 let first = COUNT.load(Ordering::SeqCst);
180 assert_eq!(first, 1);
181
182 run_registered_eager_init();
184 let second = COUNT.load(Ordering::SeqCst);
185 assert_eq!(second, 1);
186 }
187}