summaryrefslogtreecommitdiff
path: root/src/poisonable.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/poisonable.rs')
-rw-r--r--src/poisonable.rs164
1 files changed, 163 insertions, 1 deletions
diff --git a/src/poisonable.rs b/src/poisonable.rs
index 8d5a810..f9b0622 100644
--- a/src/poisonable.rs
+++ b/src/poisonable.rs
@@ -99,7 +99,7 @@ mod tests {
use super::*;
use crate::lockable::Lockable;
- use crate::{LockCollection, Mutex, ThreadKey};
+ use crate::{LockCollection, Mutex, RwLock, ThreadKey};
#[test]
fn locking_poisoned_mutex_returns_error_in_collection() {
@@ -127,6 +127,31 @@ mod tests {
}
#[test]
+ fn locking_poisoned_rwlock_returns_error_in_collection() {
+ let key = ThreadKey::get().unwrap();
+ let mutex = LockCollection::new(Poisonable::new(RwLock::new(42)));
+
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let mut guard1 = mutex.read(key);
+ let guard = guard1.as_deref_mut().unwrap();
+ assert_eq!(**guard, 42);
+ panic!();
+
+ #[allow(unreachable_code)]
+ drop(guard1);
+ })
+ .join()
+ .unwrap_err();
+ });
+
+ let error = mutex.read(key);
+ let error = error.as_deref().unwrap_err();
+ assert_eq!(***error.get_ref(), 42);
+ }
+
+ #[test]
fn non_poisoned_get_mut_is_ok() {
let mut mutex = Poisonable::new(Mutex::new(42));
let guard = mutex.get_mut();
@@ -201,6 +226,118 @@ mod tests {
}
#[test]
+ fn scoped_lock_can_poison() {
+ let key = ThreadKey::get().unwrap();
+ let mutex = Poisonable::new(Mutex::new(42));
+
+ let r = std::panic::catch_unwind(|| {
+ mutex.scoped_lock(key, |num| {
+ *num.unwrap() = 56;
+ panic!();
+ })
+ });
+ assert!(r.is_err());
+
+ let key = ThreadKey::get().unwrap();
+ assert!(mutex.is_poisoned());
+ mutex.scoped_lock(key, |num| {
+ let Err(error) = num else { panic!() };
+ mutex.clear_poison();
+ let guard = error.into_inner();
+ assert_eq!(*guard, 56);
+ });
+ assert!(!mutex.is_poisoned());
+ }
+
+ #[test]
+ fn scoped_try_lock_can_fail() {
+ let key = ThreadKey::get().unwrap();
+ let mutex = Poisonable::new(Mutex::new(42));
+ let guard = mutex.lock(key);
+
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let r = mutex.scoped_try_lock(key, |_| {});
+ assert!(r.is_err());
+ });
+ });
+
+ drop(guard);
+ }
+
+ #[test]
+ fn scoped_try_lock_can_succeed() {
+ let rwlock = Poisonable::new(RwLock::new(42));
+
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let r = rwlock.scoped_try_lock(key, |guard| {
+ assert_eq!(*guard.unwrap(), 42);
+ });
+ assert!(r.is_ok());
+ });
+ });
+ }
+
+ #[test]
+ fn scoped_read_can_poison() {
+ let key = ThreadKey::get().unwrap();
+ let mutex = Poisonable::new(RwLock::new(42));
+
+ let r = std::panic::catch_unwind(|| {
+ mutex.scoped_read(key, |num| {
+ assert_eq!(*num.unwrap(), 42);
+ panic!();
+ })
+ });
+ assert!(r.is_err());
+
+ let key = ThreadKey::get().unwrap();
+ assert!(mutex.is_poisoned());
+ mutex.scoped_read(key, |num| {
+ let Err(error) = num else { panic!() };
+ mutex.clear_poison();
+ let guard = error.into_inner();
+ assert_eq!(*guard, 42);
+ });
+ assert!(!mutex.is_poisoned());
+ }
+
+ #[test]
+ fn scoped_try_read_can_fail() {
+ let key = ThreadKey::get().unwrap();
+ let rwlock = Poisonable::new(RwLock::new(42));
+ let guard = rwlock.lock(key);
+
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let r = rwlock.scoped_try_read(key, |_| {});
+ assert!(r.is_err());
+ });
+ });
+
+ drop(guard);
+ }
+
+ #[test]
+ fn scoped_try_read_can_succeed() {
+ let rwlock = Poisonable::new(RwLock::new(42));
+
+ std::thread::scope(|s| {
+ s.spawn(|| {
+ let key = ThreadKey::get().unwrap();
+ let r = rwlock.scoped_try_read(key, |guard| {
+ assert_eq!(*guard.unwrap(), 42);
+ });
+ assert!(r.is_ok());
+ });
+ });
+ }
+
+ #[test]
fn display_works() {
let key = ThreadKey::get().unwrap();
let mutex = Poisonable::new(Mutex::new("Hello, world!"));
@@ -318,6 +455,31 @@ mod tests {
}
#[test]
+ fn clear_poison_for_poisoned_rwlock() {
+ let lock = Arc::new(Poisonable::new(RwLock::new(0)));
+ let c_mutex = Arc::clone(&lock);
+
+ let _ = std::thread::spawn(move || {
+ let key = ThreadKey::get().unwrap();
+ let lock = c_mutex.read(key).unwrap();
+ assert_eq!(*lock, 42);
+ panic!(); // the mutex gets poisoned
+ })
+ .join();
+
+ assert!(lock.is_poisoned());
+
+ let key = ThreadKey::get().unwrap();
+ let _ = lock.lock(key).unwrap_or_else(|mut e| {
+ **e.get_mut() = 1;
+ lock.clear_poison();
+ e.into_inner()
+ });
+
+ assert!(!lock.is_poisoned());
+ }
+
+ #[test]
fn error_as_ref() {
let mutex = Poisonable::new(Mutex::new("foo"));