use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
pub fn recover_read<'a, T: ?Sized>(
lock: &'a RwLock<T>,
name: &'static str,
) -> RwLockReadGuard<'a, T> {
match lock.read() {
Ok(g) => g,
Err(poisoned) => {
tracing::warn!(
lock = name,
"RwLock poisoned; reading recovered data (v0.8.4 #77)"
);
crate::metrics::record_lock_poison_recovery(name, "read");
poisoned.into_inner()
}
}
}
pub fn recover_write<'a, T: ?Sized>(
lock: &'a RwLock<T>,
name: &'static str,
) -> RwLockWriteGuard<'a, T> {
match lock.write() {
Ok(g) => g,
Err(poisoned) => {
tracing::warn!(
lock = name,
"RwLock poisoned; recovering inner state (v0.8.4 #77)"
);
crate::metrics::record_lock_poison_recovery(name, "write");
poisoned.into_inner()
}
}
}
pub fn recover_mutex<'a, T: ?Sized>(lock: &'a Mutex<T>, name: &'static str) -> MutexGuard<'a, T> {
match lock.lock() {
Ok(g) => g,
Err(poisoned) => {
tracing::warn!(
lock = name,
"Mutex poisoned; recovering inner state (v0.8.4 #77)"
);
crate::metrics::record_lock_poison_recovery(name, "mutex");
poisoned.into_inner()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
#[test]
fn recover_read_after_write_panic_returns_inner_data() {
let lock = Arc::new(RwLock::new(vec![1u32, 2, 3]));
let lock_cl = Arc::clone(&lock);
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut g = lock_cl.write().expect("clean lock");
g.push(4);
panic!("force-poison");
}));
assert!(
lock.is_poisoned(),
"panic inside write must poison the lock"
);
let g = recover_read(&lock, "test.recover_read");
assert_eq!(*g, vec![1, 2, 3, 4]);
}
#[test]
fn recover_write_after_write_panic_allows_continued_writes() {
let lock = Arc::new(RwLock::new(0u32));
let lock_cl = Arc::clone(&lock);
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut g = lock_cl.write().expect("clean lock");
*g = 42;
panic!("force-poison");
}));
assert!(lock.is_poisoned());
{
let mut g = recover_write(&lock, "test.recover_write");
*g = 100;
}
let g = recover_read(&lock, "test.recover_read_post");
assert_eq!(*g, 100);
}
#[test]
fn recover_mutex_after_lock_panic_returns_inner() {
let lock = Arc::new(Mutex::new(String::from("alpha")));
let lock_cl = Arc::clone(&lock);
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let mut g = lock_cl.lock().expect("clean lock");
g.push_str("-beta");
panic!("force-poison");
}));
assert!(lock.is_poisoned());
let g = recover_mutex(&lock, "test.recover_mutex");
assert_eq!(&*g, "alpha-beta");
}
}