1use std::sync::{Condvar, Mutex, MutexGuard, PoisonError, WaitTimeoutResult};
13use std::time::Duration;
14
15pub trait MutexExt<T> {
17 fn lock_unpoisoned(&self) -> MutexGuard<'_, T>;
19}
20
21impl<T> MutexExt<T> for Mutex<T> {
22 fn lock_unpoisoned(&self) -> MutexGuard<'_, T> {
23 self.lock().unwrap_or_else(PoisonError::into_inner)
24 }
25}
26
27pub trait CondvarExt {
29 fn wait_unpoisoned<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T>;
31
32 fn wait_timeout_unpoisoned<'a, T>(
34 &self,
35 guard: MutexGuard<'a, T>,
36 timeout: Duration,
37 ) -> (MutexGuard<'a, T>, WaitTimeoutResult);
38}
39
40impl CondvarExt for Condvar {
41 fn wait_unpoisoned<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
42 self.wait(guard).unwrap_or_else(PoisonError::into_inner)
43 }
44
45 fn wait_timeout_unpoisoned<'a, T>(
46 &self,
47 guard: MutexGuard<'a, T>,
48 timeout: Duration,
49 ) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
50 self.wait_timeout(guard, timeout)
51 .unwrap_or_else(PoisonError::into_inner)
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use std::sync::Arc;
59
60 #[test]
61 fn poisoned_locks_keep_their_last_value() {
62 let shared = Arc::new(Mutex::new(1));
63 let writer = shared.clone();
64 let panicked = std::thread::spawn(move || {
65 let mut value = writer.lock().unwrap();
66 *value = 7;
67 panic!("poison the lock");
68 })
69 .join();
70 assert!(panicked.is_err() && shared.is_poisoned());
71 assert_eq!(*shared.lock_unpoisoned(), 7);
72 let (guard, result) =
73 Condvar::new().wait_timeout_unpoisoned(shared.lock_unpoisoned(), Duration::ZERO);
74 assert_eq!(*guard, 7);
75 assert!(result.timed_out());
76 }
77}