1use std::sync::atomic::{AtomicBool, Ordering};
16
17static IN_RT_CONTEXT: AtomicBool = AtomicBool::new(false);
19
20pub fn enter_rt() {
22 IN_RT_CONTEXT.store(true, Ordering::Release);
23}
24
25pub fn exit_rt() {
27 IN_RT_CONTEXT.store(false, Ordering::Release);
28}
29
30pub fn is_rt() -> bool {
32 IN_RT_CONTEXT.load(Ordering::Acquire)
33}
34
35pub fn rt_guard<R>(f: impl FnOnce() -> R) -> R {
45 enter_rt();
46 let result = f();
47 exit_rt();
48 result
49}
50
51pub trait RtSafe: Send {}
54
55impl RtSafe for f32 {}
56impl RtSafe for f64 {}
57impl RtSafe for i32 {}
58impl RtSafe for u32 {}
59impl RtSafe for usize {}
60impl RtSafe for bool {}
61
62pub const RT_RULES: &[&str] = &[
64 "No Mutex, RwLock, or any blocking synchronization",
65 "No Box::new, Vec::push, String::from, or any heap allocation",
66 "No disk reads, network calls, or system timers",
67 "No Drop of heap objects (use basedrop for deferred reclamation)",
68 "All memory must be pre-allocated before entering the callback",
69];
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn test_rt_flag_default_off() {
77 exit_rt();
79 assert!(!is_rt());
80 }
81
82 #[test]
83 fn test_rt_guard_sets_flag() {
84 exit_rt(); let was_rt = rt_guard(|| {
86 is_rt()
87 });
88 assert!(was_rt, "Should be in RT context inside guard");
89 assert!(!is_rt(), "Should exit RT context after guard");
90 }
91
92 #[test]
93 fn test_enter_exit_manual() {
94 exit_rt();
95 assert!(!is_rt());
96 enter_rt();
97 assert!(is_rt());
98 exit_rt();
99 assert!(!is_rt());
100 }
101
102 #[test]
103 fn test_rt_rules_not_empty() {
104 assert!(!RT_RULES.is_empty());
105 assert!(RT_RULES.len() >= 4);
106 }
107
108 #[test]
109 fn test_nested_rt_guard() {
110 exit_rt();
111 rt_guard(|| {
112 assert!(is_rt());
113 let inner = is_rt();
115 assert!(inner);
116 });
117 assert!(!is_rt());
118 }
119}