diff options
Diffstat (limited to 'library/std/src/sync/poison/mutex/tests.rs')
| -rw-r--r-- | library/std/src/sync/poison/mutex/tests.rs | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/library/std/src/sync/poison/mutex/tests.rs b/library/std/src/sync/poison/mutex/tests.rs new file mode 100644 index 00000000000..395c8aada08 --- /dev/null +++ b/library/std/src/sync/poison/mutex/tests.rs @@ -0,0 +1,442 @@ +use crate::fmt::Debug; +use crate::ops::FnMut; +use crate::panic::{self, AssertUnwindSafe}; +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::mpsc::channel; +use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; +use crate::{hint, mem, thread}; + +struct Packet<T>(Arc<(Mutex<T>, Condvar)>); + +#[derive(Eq, PartialEq, Debug)] +struct NonCopy(i32); + +#[derive(Eq, PartialEq, Debug)] +struct NonCopyNeedsDrop(i32); + +impl Drop for NonCopyNeedsDrop { + fn drop(&mut self) { + hint::black_box(()); + } +} + +#[test] +fn test_needs_drop() { + assert!(!mem::needs_drop::<NonCopy>()); + assert!(mem::needs_drop::<NonCopyNeedsDrop>()); +} + +#[derive(Clone, Eq, PartialEq, Debug)] +struct Cloneable(i32); + +#[test] +fn smoke() { + let m = Mutex::new(()); + drop(m.lock().unwrap()); + drop(m.lock().unwrap()); +} + +#[test] +fn lots_and_lots() { + const J: u32 = 1000; + const K: u32 = 3; + + let m = Arc::new(Mutex::new(0)); + + fn inc(m: &Mutex<u32>) { + for _ in 0..J { + *m.lock().unwrap() += 1; + } + } + + let (tx, rx) = channel(); + for _ in 0..K { + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + } + + drop(tx); + for _ in 0..2 * K { + rx.recv().unwrap(); + } + assert_eq!(*m.lock().unwrap(), J * K * 2); +} + +#[test] +fn try_lock() { + let m = Mutex::new(()); + *m.try_lock().unwrap() = (); +} + +fn new_poisoned_mutex<T>(value: T) -> Mutex<T> { + let mutex = Mutex::new(value); + + let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard = mutex.lock().unwrap(); + + panic!("test panic to poison mutex"); + })); + + assert!(catch_unwind_result.is_err()); + assert!(mutex.is_poisoned()); + + mutex +} + +#[test] +fn test_into_inner() { + let m = Mutex::new(NonCopy(10)); + assert_eq!(m.into_inner().unwrap(), NonCopy(10)); +} + +#[test] +fn test_into_inner_drop() { + struct Foo(Arc<AtomicUsize>); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = Mutex::new(Foo(num_drops.clone())); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + { + let _inner = m.into_inner().unwrap(); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + } + assert_eq!(num_drops.load(Ordering::SeqCst), 1); +} + +#[test] +fn test_into_inner_poison() { + let m = new_poisoned_mutex(NonCopy(10)); + + match m.into_inner() { + Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), + Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] +fn test_get_cloned() { + let m = Mutex::new(Cloneable(10)); + + assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); +} + +#[test] +fn test_get_cloned_poison() { + let m = new_poisoned_mutex(Cloneable(10)); + + match m.get_cloned() { + Err(e) => assert_eq!(e.into_inner(), ()), + Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] +fn test_get_mut() { + let mut m = Mutex::new(NonCopy(10)); + *m.get_mut().unwrap() = NonCopy(20); + assert_eq!(m.into_inner().unwrap(), NonCopy(20)); +} + +#[test] +fn test_get_mut_poison() { + let mut m = new_poisoned_mutex(NonCopy(10)); + + match m.get_mut() { + Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), + Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] +fn test_set() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + m.set(value()).unwrap(); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_set_poison() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_mutex(init()); + + match m.set(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("set of poisoned Mutex is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + assert_eq!(m.replace(value()).unwrap(), init()); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_replace_poison() { + fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = new_poisoned_mutex(init()); + + match m.replace(value()) { + Err(e) => { + assert_eq!(e.into_inner(), value()); + assert_eq!(m.into_inner().unwrap_err().into_inner(), init()); + } + Ok(x) => panic!("replace of poisoned Mutex is Ok: {x:?}"), + } + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_mutex_arc_condvar() { + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + // wait until parent gets in + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let mut lock = lock.lock().unwrap(); + *lock = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + assert!(!*lock); + while !*lock { + lock = cvar.wait(lock).unwrap(); + } +} + +#[test] +fn test_arc_condvar_poison() { + let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + + let _t = thread::spawn(move || -> () { + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let _g = lock.lock().unwrap(); + cvar.notify_one(); + // Parent should fail when it wakes up. + panic!(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + while *lock == 1 { + match cvar.wait(lock) { + Ok(l) => { + lock = l; + assert_eq!(*lock, 1); + } + Err(..) => break, + } + } +} + +#[test] +fn test_mutex_arc_poison() { + let arc = Arc::new(Mutex::new(1)); + assert!(!arc.is_poisoned()); + let arc2 = arc.clone(); + let _ = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + assert_eq!(*lock, 2); // deliberate assertion failure to poison the mutex + }) + .join(); + assert!(arc.lock().is_err()); + assert!(arc.is_poisoned()); +} + +#[test] +fn test_mutex_arc_poison_mapped() { + let arc = Arc::new(Mutex::new(1)); + assert!(!arc.is_poisoned()); + let arc2 = arc.clone(); + let _ = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + let lock = MutexGuard::map(lock, |val| val); + assert_eq!(*lock, 2); // deliberate assertion failure to poison the mutex + }) + .join(); + assert!(arc.lock().is_err()); + assert!(arc.is_poisoned()); +} + +#[test] +fn test_mutex_arc_nested() { + // Tests nested mutexes and access + // to underlying data. + let arc = Arc::new(Mutex::new(1)); + let arc2 = Arc::new(Mutex::new(arc)); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + let lock2 = lock.lock().unwrap(); + assert_eq!(*lock2, 1); + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); +} + +#[test] +fn test_mutex_arc_access_in_unwind() { + let arc = Arc::new(Mutex::new(1)); + let arc2 = arc.clone(); + let _ = thread::spawn(move || -> () { + struct Unwinder { + i: Arc<Mutex<i32>>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + *self.i.lock().unwrap() += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }) + .join(); + let lock = arc.lock().unwrap(); + assert_eq!(*lock, 2); +} + +#[test] +fn test_mutex_unsized() { + let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); + { + let b = &mut *mutex.lock().unwrap(); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*mutex.lock().unwrap(), comp); +} + +#[test] +fn test_mapping_mapped_guard() { + let arr = [0; 4]; + let mut lock = Mutex::new(arr); + let guard = lock.lock().unwrap(); + let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); + let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); + assert_eq!(guard.len(), 1); + guard[0] = 42; + drop(guard); + assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); +} + +#[test] +fn panic_while_mapping_unlocked_poison() { + let lock = Mutex::new(()); + + let _ = panic::catch_unwind(|| { + let guard = lock.lock().unwrap(); + let _guard = MutexGuard::map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_lock() { + Ok(_) => panic!("panicking in a MutexGuard::map closure should poison the Mutex"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MutexGuard::map closure should unlock the mutex") + } + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.lock().unwrap(); + let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_lock() { + Ok(_) => panic!("panicking in a MutexGuard::try_map closure should poison the Mutex"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MutexGuard::try_map closure should unlock the mutex") + } + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.lock().unwrap(); + let guard = MutexGuard::map::<(), _>(guard, |val| val); + let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_lock() { + Ok(_) => panic!("panicking in a MappedMutexGuard::map closure should poison the Mutex"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MappedMutexGuard::map closure should unlock the mutex") + } + Err(TryLockError::Poisoned(_)) => {} + } + + let _ = panic::catch_unwind(|| { + let guard = lock.lock().unwrap(); + let guard = MutexGuard::map::<(), _>(guard, |val| val); + let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!()); + }); + + match lock.try_lock() { + Ok(_) => panic!("panicking in a MappedMutexGuard::try_map closure should poison the Mutex"), + Err(TryLockError::WouldBlock) => { + panic!("panicking in a MappedMutexGuard::try_map closure should unlock the mutex") + } + Err(TryLockError::Poisoned(_)) => {} + } + + drop(lock); +} |
