summaryrefslogtreecommitdiffstats
path: root/src/locks.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-26 04:20:52 +0100
committertv <tv@krebsco.de>2026-10-26 04:20:52 +0100
commit7b7dcb5a77061cb69d368de263a02b5ad26fbf51 (patch)
treed3255ce629cb60059cb55191dc206d0f64ca645e /src/locks.rs
parentd668ce2ff8f308e54c1c7a2c145cb3479cab899d (diff)
unify lock logic
Diffstat (limited to 'src/locks.rs')
-rw-r--r--src/locks.rs53
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);
+ }
+}