use std::sync::atomic::{AtomicBool, Ordering};
static IN_RT_CONTEXT: AtomicBool = AtomicBool::new(false);
pub fn enter_rt() {
IN_RT_CONTEXT.store(true, Ordering::Release);
}
pub fn exit_rt() {
IN_RT_CONTEXT.store(false, Ordering::Release);
}
pub fn is_rt() -> bool {
IN_RT_CONTEXT.load(Ordering::Acquire)
}
pub fn rt_guard<R>(f: impl FnOnce() -> R) -> R {
enter_rt();
let result = f();
exit_rt();
result
}
pub trait RtSafe: Send {}
impl RtSafe for f32 {}
impl RtSafe for f64 {}
impl RtSafe for i32 {}
impl RtSafe for u32 {}
impl RtSafe for usize {}
impl RtSafe for bool {}
pub const RT_RULES: &[&str] = &[
"No Mutex, RwLock, or any blocking synchronization",
"No Box::new, Vec::push, String::from, or any heap allocation",
"No disk reads, network calls, or system timers",
"No Drop of heap objects (use basedrop for deferred reclamation)",
"All memory must be pre-allocated before entering the callback",
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rt_flag_default_off() {
exit_rt();
assert!(!is_rt());
}
#[test]
fn test_rt_guard_sets_flag() {
exit_rt(); let was_rt = rt_guard(|| {
is_rt()
});
assert!(was_rt, "Should be in RT context inside guard");
assert!(!is_rt(), "Should exit RT context after guard");
}
#[test]
fn test_enter_exit_manual() {
exit_rt();
assert!(!is_rt());
enter_rt();
assert!(is_rt());
exit_rt();
assert!(!is_rt());
}
#[test]
fn test_rt_rules_not_empty() {
assert!(!RT_RULES.is_empty());
assert!(RT_RULES.len() >= 4);
}
#[test]
fn test_nested_rt_guard() {
exit_rt();
rt_guard(|| {
assert!(is_rt());
let inner = is_rt();
assert!(inner);
});
assert!(!is_rt());
}
}