//! Std locks without poisoning. A panic while one is held leaves its data as it was, and //! every lock of this kind here guards counters, tables or caches that stay usable: so //! the next user takes the data as it is instead of panicking too. (The tokio locks //! around the document and the peers don't poison.) use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; pub(crate) trait MutexExt { /// Locks, recovering from a poisoned mutex. fn lock_or_recover(&self) -> MutexGuard<'_, T>; } impl MutexExt for Mutex { fn lock_or_recover(&self) -> MutexGuard<'_, T> { self.lock().unwrap_or_else(|poisoned| poisoned.into_inner()) } } pub(crate) trait RwLockExt { /// Locks for reading, recovering from a poisoned lock. fn read_or_recover(&self) -> RwLockReadGuard<'_, T>; /// Locks for writing, recovering from a poisoned lock. fn write_or_recover(&self) -> RwLockWriteGuard<'_, T>; } impl RwLockExt for RwLock { fn read_or_recover(&self) -> RwLockReadGuard<'_, T> { self.read().unwrap_or_else(|poisoned| poisoned.into_inner()) } fn write_or_recover(&self) -> RwLockWriteGuard<'_, T> { self.write() .unwrap_or_else(|poisoned| poisoned.into_inner()) } } #[cfg(test)] mod tests { use super::*; #[test] fn a_panic_while_locked_doesnt_spread() { let mutex = std::sync::Arc::new(Mutex::new(1)); let poisoner = std::sync::Arc::clone(&mutex); let _ = std::thread::spawn(move || { let _guard = poisoner.lock().unwrap(); panic!("while holding the lock"); }) .join(); assert!(mutex.is_poisoned()); assert_eq!(*mutex.lock_or_recover(), 1); } }