Skip to main content

microscope_memory/
sync_guard.rs

1//! Poison-safe lock accessors.
2//!
3//! `std::sync` locks become poisoned when a thread panics while holding them;
4//! afterwards every `lock()` panics again, permanently breaking the subsystem
5//! for the rest of the process lifetime. These helpers recover the guard
6//! instead, so one stray panic cannot take down unrelated code.
7
8use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
9
10/// Acquire a read guard, recovering from a poisoned lock.
11pub fn read_lock<T>(lock: &RwLock<T>) -> RwLockReadGuard<'_, T> {
12    lock.read().unwrap_or_else(|e| e.into_inner())
13}
14
15/// Acquire a write guard, recovering from a poisoned lock.
16pub fn write_lock<T>(lock: &RwLock<T>) -> RwLockWriteGuard<'_, T> {
17    lock.write().unwrap_or_else(|e| e.into_inner())
18}
19
20/// Acquire a mutex guard, recovering from a poisoned lock.
21pub fn mutex_lock<T>(lock: &Mutex<T>) -> MutexGuard<'_, T> {
22    lock.lock().unwrap_or_else(|e| e.into_inner())
23}
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28
29    #[test]
30    fn helpers_recover_from_poisoned_locks() {
31        let rw = RwLock::new(1u32);
32        let _ = std::panic::catch_unwind(|| {
33            let _guard = crate::sync_guard::write_lock(&rw);
34            panic!("boom");
35        });
36        assert!(rw.is_poisoned());
37        assert_eq!(*read_lock(&rw), 1);
38        *write_lock(&rw) = 2;
39        assert_eq!(*read_lock(&rw), 2);
40
41        let m = Mutex::new(3u32);
42        let _ = std::panic::catch_unwind(|| {
43            let _guard = crate::sync_guard::mutex_lock(&m);
44            panic!("boom");
45        });
46        assert_eq!(*mutex_lock(&m), 3);
47    }
48}