Skip to main content

zenith_foundation/
sync.rs

1//! 同步原语辅助(全 workspace 唯一实现)
2//!
3//! [`lock_recover`]:锁中毒恢复的统一入口。`Mutex` 中毒(持锁线程 panic)
4//! 不代表数据必然损坏——Zenith 的共享状态(缓存分片、指标、审计缓冲)
5//! 均为可独立校验的一致性单元,恢复访问优于全链路 fail-stop。
6//! 恢复行为集中在此函数,禁止各 crate 内联 `unwrap_or_else(|e| e.into_inner())`。
7
8use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
9
10/// 获取互斥锁,中毒时恢复内部数据(poison-tolerant)
11///
12/// # 语义
13/// - 未中毒:等价于 `lock().unwrap()` 的正常路径(零额外开销)
14/// - 已中毒:取回 `PoisonError` 内的守卫,继续服务(fail-operational)
15///
16/// # 适用约束
17/// 仅用于"数据可独立校验、单条损坏不扩散"的共享状态;
18/// 涉及资金/密钥等强一致性场景禁止使用,应让错误沿 `Result` 传播。
19#[inline]
20pub fn lock_recover<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
21    mutex.lock().unwrap_or_else(|e| e.into_inner())
22}
23
24/// 获取 RwLock 读锁,中毒时恢复(语义同 [`lock_recover`])
25///
26/// 用于读多写少场景(如 WAF 引擎规则集):检查路径并发持读锁,
27/// 规则热更新才走写锁,消除全局串行点。
28#[inline]
29pub fn read_recover<T>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
30    lock.read().unwrap_or_else(|e| e.into_inner())
31}
32
33/// 获取 RwLock 写锁,中毒时恢复(语义同 [`lock_recover`])
34#[inline]
35pub fn write_recover<T>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
36    lock.write().unwrap_or_else(|e| e.into_inner())
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use std::sync::Arc;
43
44    #[test]
45    fn test_lock_recover_normal() {
46        let m = Mutex::new(42u64);
47        {
48            let mut g = lock_recover(&m);
49            *g += 1;
50        }
51        assert_eq!(*lock_recover(&m), 43);
52    }
53
54    #[test]
55    fn test_lock_recover_poisoned() {
56        let m = Arc::new(Mutex::new(1u64));
57        let m2 = Arc::clone(&m);
58        // 制造中毒:持锁 panic
59        let _ = std::thread::spawn(move || {
60            let mut g = m2.lock().expect("首次加锁应成功");
61            *g = 99;
62            panic!("intentional poison for test");
63        })
64        .join();
65        // 中毒后仍可恢复访问,且数据为 panic 前写入的值
66        assert_eq!(*lock_recover(&m), 99);
67    }
68}