zenith-foundation 0.1.0

Zenith 核心基础设施:统一错误类型、FrameToken 所有权令牌、FramePool、分层资源账本、恒定时间比较
Documentation
//! 同步原语辅助(全 workspace 唯一实现)
//!
//! [`lock_recover`]:锁中毒恢复的统一入口。`Mutex` 中毒(持锁线程 panic)
//! 不代表数据必然损坏——Zenith 的共享状态(缓存分片、指标、审计缓冲)
//! 均为可独立校验的一致性单元,恢复访问优于全链路 fail-stop。
//! 恢复行为集中在此函数,禁止各 crate 内联 `unwrap_or_else(|e| e.into_inner())`。

use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};

/// 获取互斥锁,中毒时恢复内部数据(poison-tolerant)
///
/// # 语义
/// - 未中毒:等价于 `lock().unwrap()` 的正常路径(零额外开销)
/// - 已中毒:取回 `PoisonError` 内的守卫,继续服务(fail-operational)
///
/// # 适用约束
/// 仅用于"数据可独立校验、单条损坏不扩散"的共享状态;
/// 涉及资金/密钥等强一致性场景禁止使用,应让错误沿 `Result` 传播。
#[inline]
pub fn lock_recover<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
    mutex.lock().unwrap_or_else(|e| e.into_inner())
}

/// 获取 RwLock 读锁,中毒时恢复(语义同 [`lock_recover`])
///
/// 用于读多写少场景(如 WAF 引擎规则集):检查路径并发持读锁,
/// 规则热更新才走写锁,消除全局串行点。
#[inline]
pub fn read_recover<T>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
    lock.read().unwrap_or_else(|e| e.into_inner())
}

/// 获取 RwLock 写锁,中毒时恢复(语义同 [`lock_recover`])
#[inline]
pub fn write_recover<T>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
    lock.write().unwrap_or_else(|e| e.into_inner())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::Arc;

    #[test]
    fn test_lock_recover_normal() {
        let m = Mutex::new(42u64);
        {
            let mut g = lock_recover(&m);
            *g += 1;
        }
        assert_eq!(*lock_recover(&m), 43);
    }

    #[test]
    fn test_lock_recover_poisoned() {
        let m = Arc::new(Mutex::new(1u64));
        let m2 = Arc::clone(&m);
        // 制造中毒:持锁 panic
        let _ = std::thread::spawn(move || {
            let mut g = m2.lock().expect("首次加锁应成功");
            *g = 99;
            panic!("intentional poison for test");
        })
        .join();
        // 中毒后仍可恢复访问,且数据为 panic 前写入的值
        assert_eq!(*lock_recover(&m), 99);
    }
}