use super::*;
contended!(
mutex_excludes_and_loses_no_wakeup,
mutex_excludes_and_loses_no_wakeup_at_bound_2,
10,
|| {
let mutex = Arc::new(Mutex::new(UnsafeCell::new(0usize)));
let held = mutex.try_lock().expect("free");
let others: Vec<_> = (0..2)
.map(|_| {
let mutex = Arc::clone(&mutex);
thread::spawn(move || bump(&block_on(mutex.lock())))
})
.collect();
bump(&held);
drop(held);
for other in others {
other.join().expect("locker");
}
assert_eq!(read(&mutex.try_lock().expect("free at the end")), 3);
},
|| {
let mutex = Arc::new(Mutex::new(UnsafeCell::new(0usize)));
let held = mutex.try_lock().expect("free");
let waiter = {
let mutex = Arc::clone(&mutex);
thread::spawn(move || bump(&block_on(mutex.lock())))
};
let barger = {
let mutex = Arc::clone(&mutex);
thread::spawn(move || mutex.try_lock().map(|guard| bump(&guard)).is_ok())
};
bump(&held);
drop(held);
waiter.join().expect("waiter");
let barged = barger.join().expect("barger");
assert_eq!(
read(&mutex.try_lock().expect("free at the end")),
2 + usize::from(barged)
);
}
);
#[test]
fn a_waiter_barged_past_max_bypass_times_is_handed_the_mutex() {
explore(
"a_waiter_barged_past_max_bypass_times_is_handed_the_mutex",
2,
10,
|| {
let mutex = Arc::new(Mutex::new(UnsafeCell::new(0usize)));
let mut held = Some(mutex.try_lock().expect("free"));
let queued = Arc::new(AtomicBool::new(false));
let waiter = {
let (mutex, flag) = (Arc::clone(&mutex), Arc::clone(&queued));
thread::spawn(move || bump(&block_on(flag_when_queued(mutex.lock(), &flag))))
};
wait_for(&queued);
let mut barged = 0;
for _ in 0..=MAX_BYPASS {
drop(held.take());
held = mutex.try_lock().ok();
if let Some(guard) = &held {
bump(guard);
barged += 1;
}
}
drop(held);
waiter.join().expect("waiter");
assert_eq!(read(&mutex.try_lock().expect("free")), barged + 1);
},
);
}
contended!(
a_cancelled_mutex_waiter_racing_the_handoff_loses_nothing,
a_cancelled_mutex_waiter_racing_the_handoff_loses_nothing_at_bound_2,
10,
|| {
let mutex = Arc::new(Mutex::new(UnsafeCell::new(0usize)));
let held = mutex.try_lock().expect("free");
let canceller = {
let mutex = Arc::clone(&mutex);
thread::spawn(move || poll_once_then_drop(mutex.lock()))
};
let waiter = {
let mutex = Arc::clone(&mutex);
thread::spawn(move || bump(&block_on(mutex.lock())))
};
drop(held);
canceller.join().expect("canceller");
waiter.join().expect("waiter");
assert_eq!(read(&mutex.try_lock().expect("the lock was not leaked")), 1);
},
|| {
let mutex = Arc::new(Mutex::new(UnsafeCell::new(0usize)));
let held = mutex.try_lock().expect("free");
let queued = Arc::new(AtomicBool::new(false));
let canceller = {
let (mutex, flag) = (Arc::clone(&mutex), Arc::clone(&queued));
thread::spawn(move || poll_once_then_drop(flag_when_queued(mutex.lock(), &flag)))
};
wait_for(&queued);
let waiter = {
let mutex = Arc::clone(&mutex);
thread::spawn(move || bump(&block_on(mutex.lock())))
};
drop(held);
canceller.join().expect("canceller");
waiter.join().expect("waiter");
assert_eq!(read(&mutex.try_lock().expect("the lock was not leaked")), 1);
}
);
#[test]
#[should_panic(expected = "a release went to a caller ahead of the queued waiter")]
fn calibration_a_queued_waiter_can_be_barged_past() {
explore(
"calibration_a_queued_waiter_can_be_barged_past",
3,
1,
|| {
let mutex = Arc::new(Mutex::new(()));
let held = mutex.try_lock().expect("free");
let queued = Arc::new(AtomicBool::new(false));
let waiter = {
let (mutex, flag) = (Arc::clone(&mutex), Arc::clone(&queued));
thread::spawn(move || drop(block_on(flag_when_queued(mutex.lock(), &flag))))
};
wait_for(&queued);
drop(held);
let barged = mutex.try_lock().is_ok();
waiter.join().expect("waiter");
assert!(
!barged,
"a release went to a caller ahead of the queued waiter"
);
},
);
}