diff options
| author | tv <tv@krebsco.de> | 2026-10-26 04:20:52 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-26 04:20:52 +0100 |
| commit | 7b7dcb5a77061cb69d368de263a02b5ad26fbf51 (patch) | |
| tree | d3255ce629cb60059cb55191dc206d0f64ca645e /src/locks.rs | |
| parent | d668ce2ff8f308e54c1c7a2c145cb3479cab899d (diff) | |
unify lock logic
Diffstat (limited to 'src/locks.rs')
| -rw-r--r-- | src/locks.rs | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/src/locks.rs b/src/locks.rs new file mode 100644 index 0000000..ad66e6f --- /dev/null +++ b/src/locks.rs @@ -0,0 +1,53 @@ +//! 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<T> { + /// Locks, recovering from a poisoned mutex. + fn lock_or_recover(&self) -> MutexGuard<'_, T>; +} + +impl<T> MutexExt<T> for Mutex<T> { + fn lock_or_recover(&self) -> MutexGuard<'_, T> { + self.lock().unwrap_or_else(|poisoned| poisoned.into_inner()) + } +} + +pub(crate) trait RwLockExt<T> { + /// 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<T> RwLockExt<T> for RwLock<T> { + 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); + } +} |
