Skip to content
Merged
Prev Previous commit
Next Next commit
reorder mutex tests
This commit simply helps discern the actual changes needed to test both poison and nonpoison locks.
  • Loading branch information
connortsui20 committed Jul 29, 2025
commit f34fb05923ab4c8bff48bf5d52ca08c5b8e2233a
302 changes: 158 additions & 144 deletions library/std/tests/sync/mutex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,9 @@ use std::sync::mpsc::channel;
use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError};
use std::{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);
////////////////////////////////////////////////////////////////////////////////////////////////////
// Nonpoison & Poison Tests
////////////////////////////////////////////////////////////////////////////////////////////////////

#[test]
fn smoke() {
Expand Down Expand Up @@ -78,19 +59,22 @@ fn try_lock() {
*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();
#[derive(Eq, PartialEq, Debug)]
struct NonCopy(i32);

panic!("test panic to poison mutex");
}));
#[derive(Eq, PartialEq, Debug)]
struct NonCopyNeedsDrop(i32);

assert!(catch_unwind_result.is_err());
assert!(mutex.is_poisoned());
impl Drop for NonCopyNeedsDrop {
fn drop(&mut self) {
hint::black_box(());
}
}

mutex
#[test]
fn test_needs_drop() {
assert!(!mem::needs_drop::<NonCopy>());
assert!(mem::needs_drop::<NonCopyNeedsDrop>());
}

#[test]
Expand All @@ -117,6 +101,143 @@ fn test_into_inner_drop() {
assert_eq!(num_drops.load(Ordering::SeqCst), 1);
}

#[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_cloned() {
#[derive(Clone, Eq, PartialEq, Debug)]
struct Cloneable(i32);

let m = Mutex::new(Cloneable(10));

assert_eq!(m.get_cloned().unwrap(), Cloneable(10));
}

#[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_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_mutex_arc_condvar() {
struct Packet<T>(Arc<(Mutex<T>, 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_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_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]);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Poison Tests
////////////////////////////////////////////////////////////////////////////////////////////////////

/// Creates a mutex that is immediately poisoned.
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]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_into_inner_poison() {
Expand All @@ -128,16 +249,12 @@ fn test_into_inner_poison() {
}
}

#[test]
fn test_get_cloned() {
let m = Mutex::new(Cloneable(10));

assert_eq!(m.get_cloned().unwrap(), Cloneable(10));
}

#[test]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_get_cloned_poison() {
#[derive(Clone, Eq, PartialEq, Debug)]
struct Cloneable(i32);

let m = new_poisoned_mutex(Cloneable(10));

match m.get_cloned() {
Expand All @@ -146,13 +263,6 @@ fn test_get_cloned_poison() {
}
}

#[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]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_get_mut_poison() {
Expand All @@ -164,23 +274,6 @@ fn test_get_mut_poison() {
}
}

#[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]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_set_poison() {
Expand All @@ -203,23 +296,6 @@ fn test_set_poison() {
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]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_replace_poison() {
Expand All @@ -242,32 +318,11 @@ fn test_replace_poison() {
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]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_arc_condvar_poison() {
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);

let packet = Packet(Arc::new((Mutex::new(1), Condvar::new())));
let packet2 = Packet(packet.0.clone());
let (tx, rx) = channel();
Expand Down Expand Up @@ -326,22 +381,6 @@ fn test_mutex_arc_poison_mapped() {
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]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_mutex_arc_access_in_unwind() {
Expand All @@ -364,31 +403,6 @@ fn test_mutex_arc_access_in_unwind() {
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]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn panic_while_mapping_unlocked_poison() {
Expand Down