use std::{
fmt,
future::Future,
hint,
panic::{AssertUnwindSafe, catch_unwind},
pin::{Pin, pin},
sync::{
Arc, Barrier,
atomic::{AtomicBool, AtomicUsize, Ordering},
},
task::{Context, Poll, Wake, Waker},
thread,
};
use futures::executor::block_on;
use ntest::timeout;
use crate::lock::{Mutex, MutexGuard, PATIENCE, RwLock, RwLockWriteGuard};
mod barrier;
mod once_cell;
mod semaphore;
#[test]
fn a_mutex_admits_one_holder_at_a_time() {
let mutex = Mutex::new(0);
let guard = ready(mutex.lock());
let mut second = Box::pin(mutex.lock());
assert!(poll_once(&mut second).is_pending());
drop(guard);
assert!(poll_once(&mut second).is_ready());
}
#[test]
fn a_newcomer_takes_a_free_mutex_ahead_of_a_waiter() {
let mutex = Mutex::new(());
let guard = ready(mutex.lock());
let mut waiter = Box::pin(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
drop(guard);
let mut newcomer = Box::pin(mutex.lock());
let Poll::Ready(newcomer) = poll_once(&mut newcomer) else {
panic!("a free mutex is taken at once, whoever waits for it");
};
assert!(poll_once(&mut waiter).is_pending());
drop(newcomer);
assert!(poll_once(&mut waiter).is_ready());
}
#[test]
fn a_dropped_lock_future_does_not_strand_the_next_waiter() {
let mutex = Mutex::new(());
let guard = ready(mutex.lock());
let mut first = Box::pin(mutex.lock());
assert!(poll_once(&mut first).is_pending());
let mut second = Box::pin(mutex.lock());
assert!(poll_once(&mut second).is_pending());
drop(guard);
drop(first);
assert!(poll_once(&mut second).is_ready());
}
#[test]
fn a_waiter_that_waited_long_holds_newcomers_back_from_a_mutex() {
let mutex = Mutex::new(());
let holder = ready(mutex.lock());
let mut waiter = Box::pin(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = ready(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert!(mutex.try_lock().is_none());
let mut newcomer = Box::pin(mutex.lock());
assert!(poll_once(&mut newcomer).is_pending());
let Poll::Ready(guard) = poll_once(&mut waiter) else {
panic!("the release of the barging guard lets the starved waiter in");
};
drop(guard);
assert!(poll_once(&mut newcomer).is_ready());
assert!(mutex.try_lock().is_some());
}
#[test]
fn a_starved_lock_future_dropped_lets_newcomers_take_the_mutex_again() {
let mutex = Mutex::new(());
let holder = ready(mutex.lock());
let mut waiter = Box::pin(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = ready(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert!(mutex.try_lock().is_none());
drop(waiter);
assert!(mutex.try_lock().is_some());
}
#[test]
fn a_starved_lock_future_dropped_after_its_wake_wakes_the_newcomer_behind_it() {
let mutex = Mutex::new(());
let holder = ready(mutex.lock());
let mut waiter = Box::pin(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = ready(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
let mut newcomer = Box::pin(mutex.lock());
assert!(poll_once(&mut newcomer).is_pending());
drop(barging);
drop(waiter);
assert!(poll_once(&mut newcomer).is_ready());
}
#[test]
fn a_taker_waiting_already_takes_the_mutex_ahead_of_a_starved_one() {
let mutex = Mutex::new(());
let holder = ready(mutex.lock());
let mut starved = Box::pin(mutex.lock());
assert!(poll_once(&mut starved).is_pending());
let mut waiting = Box::pin(mutex.lock());
assert!(poll_once(&mut waiting).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = ready(mutex.lock());
assert!(poll_once(&mut starved).is_pending());
drop(barging);
assert!(mutex.try_lock().is_none());
let Poll::Ready(guard) = poll_once(&mut waiting) else {
panic!("a taker that was waiting already takes the free mutex");
};
assert!(poll_once(&mut starved).is_pending());
drop(guard);
assert!(poll_once(&mut starved).is_ready());
}
#[test]
fn try_lock_takes_a_free_mutex_and_fails_on_a_held_one() {
let mutex = Mutex::new(1);
let mut guard = mutex.try_lock().expect("a new mutex is free");
*guard = 2;
assert!(mutex.try_lock().is_none());
drop(guard);
let guard = ready(mutex.lock());
assert!(mutex.try_lock().is_none());
drop(guard);
assert_eq!(*mutex.try_lock().expect("a released mutex is free"), 2);
}
#[test]
fn a_mutex_arc_guard_outlives_its_borrow_and_goes_to_another_thread() {
let mutex = Arc::new(Mutex::new(1));
let mut guard = ready(mutex.lock_arc());
assert!(mutex.try_lock().is_none());
thread::spawn(move || *guard = 2).join().unwrap();
assert_eq!(
*mutex
.try_lock()
.expect("the other thread let go of the mutex"),
2
);
}
#[test]
fn try_lock_arc_fails_on_a_held_mutex_and_takes_a_free_one() {
let mutex = Arc::new(Mutex::new(1));
let guard = ready(mutex.lock());
assert!(mutex.try_lock_arc().is_none());
drop(guard);
let mut guard = mutex.try_lock_arc().expect("a released mutex is free");
*guard = 2;
assert!(mutex.try_lock_arc().is_none());
assert!(mutex.try_lock().is_none());
drop(guard);
assert_eq!(*mutex.try_lock_arc().expect("a released mutex is free"), 2);
}
#[test]
fn dropping_a_mutex_arc_guard_lets_a_waiter_in() {
let mutex = Arc::new(Mutex::new(()));
let guard = ready(mutex.lock_arc());
let mut borrowing = Box::pin(mutex.lock());
assert!(poll_once(&mut borrowing).is_pending());
let mut owning = Box::pin(mutex.lock_arc());
assert!(poll_once(&mut owning).is_pending());
drop(guard);
let Poll::Ready(guard) = poll_once(&mut borrowing) else {
panic!("the release of the Arc guard lets the first waiter in");
};
assert!(poll_once(&mut owning).is_pending());
drop(guard);
assert!(poll_once(&mut owning).is_ready());
}
#[test]
fn a_lock_arc_that_waited_long_holds_newcomers_back() {
let mutex = Arc::new(Mutex::new(()));
let holder = ready(mutex.lock_arc());
let mut waiter = Box::pin(mutex.lock_arc());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = mutex
.try_lock_arc()
.expect("a free mutex is taken, whoever waits for it");
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert!(mutex.try_lock_arc().is_none());
let mut newcomer = Box::pin(mutex.lock_arc());
assert!(poll_once(&mut newcomer).is_pending());
let Poll::Ready(guard) = poll_once(&mut waiter) else {
panic!("the release of the barging guard lets the starved waiter in");
};
drop(guard);
assert!(poll_once(&mut newcomer).is_ready());
}
#[test]
fn readers_share_and_a_writer_excludes() {
let lock = RwLock::new(1);
let first = ready(lock.read());
let second = ready(lock.read());
let mut writer = Box::pin(lock.write());
assert!(poll_once(&mut writer).is_pending());
drop(first);
drop(second);
let Poll::Ready(mut guard) = poll_once(&mut writer) else {
panic!("the last reader lets the writer in");
};
*guard = 2;
let mut reader = Box::pin(lock.read());
assert!(poll_once(&mut reader).is_pending());
drop(guard);
let Poll::Ready(guard) = poll_once(&mut reader) else {
panic!("the writer's release lets readers in");
};
assert_eq!(*guard, 2);
}
#[test]
fn a_writer_release_lets_every_waiting_reader_in() {
let lock = RwLock::new(());
let writer = lock.try_write().expect("a new lock is free to write");
let mut first = Box::pin(lock.read());
let mut second = Box::pin(lock.read());
assert!(poll_once(&mut first).is_pending());
assert!(poll_once(&mut second).is_pending());
drop(writer);
assert!(poll_once(&mut first).is_ready());
assert!(poll_once(&mut second).is_ready());
}
#[test]
fn a_waiting_writer_holds_new_readers_back() {
let lock = RwLock::new(());
let reader = ready(lock.read());
let mut writer = Box::pin(lock.write());
assert!(poll_once(&mut writer).is_pending());
let mut late_reader = Box::pin(lock.read());
assert!(poll_once(&mut late_reader).is_pending());
drop(reader);
assert!(poll_once(&mut writer).is_ready());
}
#[test]
fn a_dropped_write_future_does_not_strand_the_next_writer() {
let lock = RwLock::new(());
let guard = ready(lock.write());
let mut first = Box::pin(lock.write());
assert!(poll_once(&mut first).is_pending());
let mut second = Box::pin(lock.write());
assert!(poll_once(&mut second).is_pending());
drop(guard);
drop(first);
assert!(poll_once(&mut second).is_ready());
}
#[test]
fn a_cancelled_writer_lets_readers_in_again() {
let lock = RwLock::new(());
let reader = ready(lock.read());
let mut writer = Box::pin(lock.write());
assert!(poll_once(&mut writer).is_pending());
drop(writer);
assert!(poll_once(&mut Box::pin(lock.read())).is_ready());
drop(reader);
}
#[test]
fn a_cancelled_writer_wakes_every_reader_it_held_back() {
let lock = RwLock::new(());
let reader = ready(lock.read());
let mut writer = Box::pin(lock.write());
assert!(poll_once(&mut writer).is_pending());
let mut first = Box::pin(lock.read());
let mut second = Box::pin(lock.read());
assert!(poll_once(&mut first).is_pending());
assert!(poll_once(&mut second).is_pending());
drop(writer);
assert!(poll_once(&mut first).is_ready());
assert!(poll_once(&mut second).is_ready());
drop(reader);
}
#[test]
fn try_read_and_try_write_take_a_free_lock_and_fail_on_a_held_one() {
let lock = RwLock::new(1);
drop(lock.try_read().expect("a new lock is free to read"));
drop(lock.try_write().expect("a new lock is free to write"));
let first = lock.try_read().expect("a free lock is read");
let second = lock.try_read().expect("readers share the lock");
assert!(lock.try_write().is_none());
drop(first);
assert!(lock.try_write().is_none());
drop(second);
let mut writer = lock.try_write().expect("the readers are gone");
*writer = 2;
assert!(lock.try_read().is_none());
assert!(lock.try_write().is_none());
drop(writer);
assert_eq!(*lock.try_read().expect("the writer is gone"), 2);
}
#[test]
fn try_read_fails_while_a_writer_waits() {
let lock = RwLock::new(());
let reader = lock.try_read().expect("a new lock is free to read");
let mut writer = Box::pin(lock.write());
assert!(poll_once(&mut writer).is_pending());
assert!(lock.try_read().is_none());
drop(reader);
assert!(lock.try_read().is_none());
let Poll::Ready(guard) = poll_once(&mut writer) else {
panic!("the last reader lets the writer in");
};
drop(guard);
assert!(lock.try_read().is_some());
}
#[test]
fn try_write_takes_a_free_lock_while_a_writer_waits() {
let lock = RwLock::new(1);
let holder = ready(lock.write());
let mut waiting = Box::pin(lock.write());
assert!(poll_once(&mut waiting).is_pending());
drop(holder);
let mut barging = lock
.try_write()
.expect("a free lock is taken, whoever waits for it");
*barging = 2;
assert!(poll_once(&mut waiting).is_pending());
drop(barging);
let Poll::Ready(guard) = poll_once(&mut waiting) else {
panic!("the release of the barging guard lets the waiting writer in");
};
assert_eq!(*guard, 2);
}
#[test]
fn a_writer_that_waited_long_holds_newcomer_writers_back() {
let lock = RwLock::new(());
let holder = ready(lock.write());
let mut waiter = Box::pin(lock.write());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = ready(lock.write());
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert!(lock.try_write().is_none());
let mut newcomer = Box::pin(lock.write());
assert!(poll_once(&mut newcomer).is_pending());
let Poll::Ready(guard) = poll_once(&mut waiter) else {
panic!("the release of the barging guard lets the starved writer in");
};
drop(guard);
assert!(poll_once(&mut newcomer).is_ready());
assert!(lock.try_write().is_some());
}
#[test]
fn a_starved_write_future_dropped_lets_newcomer_writers_in_again() {
let lock = RwLock::new(());
let holder = ready(lock.write());
let mut waiter = Box::pin(lock.write());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = ready(lock.write());
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert!(lock.try_write().is_none());
drop(waiter);
assert!(lock.try_write().is_some());
}
#[test]
fn a_reader_that_waited_long_enters_ahead_of_a_waiting_writer() {
let lock = RwLock::new(());
let first = ready(lock.write());
let mut reader = Box::pin(lock.read());
assert!(poll_once(&mut reader).is_pending());
let mut writer = Box::pin(lock.write());
assert!(poll_once(&mut writer).is_pending());
thread::sleep(PATIENCE);
drop(first);
let Poll::Ready(guard) = poll_once(&mut reader) else {
panic!("a reader that waited for long enters, though a writer waits for the lock");
};
assert!(poll_once(&mut writer).is_pending());
assert!(lock.try_read().is_none());
drop(guard);
assert!(poll_once(&mut writer).is_ready());
}
#[test]
fn a_starved_reader_holds_every_writer_back_until_it_is_in() {
let lock = RwLock::new(());
let first = ready(lock.write());
let mut reader = Box::pin(lock.read());
assert!(poll_once(&mut reader).is_pending());
let mut second = Box::pin(lock.write());
assert!(poll_once(&mut second).is_pending());
thread::sleep(PATIENCE);
drop(first);
let Poll::Ready(second) = poll_once(&mut second) else {
panic!("the release of the first writer lets the second in");
};
assert!(poll_once(&mut reader).is_pending());
let mut third = Box::pin(lock.write());
assert!(poll_once(&mut third).is_pending());
drop(second);
assert!(poll_once(&mut third).is_pending());
assert!(lock.try_write().is_none());
let Poll::Ready(guard) = poll_once(&mut reader) else {
panic!("the release of the second writer lets the starved reader in");
};
drop(guard);
assert!(poll_once(&mut third).is_ready());
}
#[test]
fn a_starved_read_future_dropped_lets_the_writers_in() {
let lock = RwLock::new(());
let first = ready(lock.write());
let mut reader = Box::pin(lock.read());
assert!(poll_once(&mut reader).is_pending());
let mut second = Box::pin(lock.write());
assert!(poll_once(&mut second).is_pending());
thread::sleep(PATIENCE);
drop(first);
let Poll::Ready(second) = poll_once(&mut second) else {
panic!("the release of the first writer lets the second in");
};
assert!(poll_once(&mut reader).is_pending());
let mut third = Box::pin(lock.write());
assert!(poll_once(&mut third).is_pending());
drop(second);
assert!(poll_once(&mut third).is_pending());
drop(reader);
assert!(poll_once(&mut third).is_ready());
}
#[test]
fn a_starved_reader_goes_ahead_of_a_starved_writer() {
let lock = RwLock::new(());
let first = ready(lock.write());
let mut reader = Box::pin(lock.read());
assert!(poll_once(&mut reader).is_pending());
let mut writer = Box::pin(lock.write());
assert!(poll_once(&mut writer).is_pending());
thread::sleep(PATIENCE);
drop(first);
let barging = lock
.try_write()
.expect("a free lock is taken, whoever waits for it");
assert!(poll_once(&mut writer).is_pending());
assert!(poll_once(&mut reader).is_pending());
drop(barging);
assert!(lock.try_write().is_none());
assert!(poll_once(&mut writer).is_pending());
let Poll::Ready(guard) = poll_once(&mut reader) else {
panic!("the starved reader enters ahead of the starved writer");
};
assert!(lock.try_read().is_none());
drop(guard);
assert!(poll_once(&mut writer).is_ready());
}
#[test]
fn rwlock_arc_guards_outlive_their_borrow_and_go_to_other_threads() {
let lock = Arc::new(RwLock::new(1));
let mut writer = ready(lock.write_arc());
assert!(lock.try_read().is_none());
thread::spawn(move || *writer = 2).join().unwrap();
let readers: Vec<_> = (0..2)
.map(|_| {
let reader = ready(lock.read_arc());
thread::spawn(move || *reader)
})
.collect();
for reader in readers {
assert_eq!(reader.join().unwrap(), 2);
}
assert!(lock.try_write().is_some());
}
#[test]
fn read_arc_guards_share_and_a_write_arc_guard_excludes() {
let lock = Arc::new(RwLock::new(1));
let first = ready(lock.read_arc());
let second = ready(lock.read());
let third = ready(lock.read_arc());
let mut writer = Box::pin(lock.write_arc());
assert!(poll_once(&mut writer).is_pending());
drop(first);
drop(second);
assert!(poll_once(&mut writer).is_pending());
drop(third);
let Poll::Ready(mut guard) = poll_once(&mut writer) else {
panic!("the last reader lets the writer in");
};
*guard = 2;
let mut owning = Box::pin(lock.read_arc());
assert!(poll_once(&mut owning).is_pending());
let mut borrowing = Box::pin(lock.read());
assert!(poll_once(&mut borrowing).is_pending());
drop(guard);
let Poll::Ready(owning) = poll_once(&mut owning) else {
panic!("the writer's release lets readers in");
};
let Poll::Ready(borrowing) = poll_once(&mut borrowing) else {
panic!("the writer's release lets every reader in");
};
assert_eq!((*owning, *borrowing), (2, 2));
}
#[test]
fn try_read_arc_and_try_write_arc_take_a_free_lock_and_fail_on_a_held_one() {
let lock = Arc::new(RwLock::new(1));
let first = lock.try_read_arc().expect("a new lock is free to read");
let second = lock.try_read_arc().expect("readers share the lock");
assert!(lock.try_write_arc().is_none());
let mut writer = Box::pin(lock.write());
assert!(poll_once(&mut writer).is_pending());
assert!(lock.try_read_arc().is_none());
drop(writer);
drop(first);
assert!(lock.try_write_arc().is_none());
drop(second);
let mut writer = lock.try_write_arc().expect("the readers are gone");
*writer = 2;
assert!(lock.try_read_arc().is_none());
assert!(lock.try_write_arc().is_none());
assert!(lock.try_read().is_none());
drop(writer);
assert_eq!(*lock.try_read_arc().expect("the writer is gone"), 2);
}
#[test]
fn a_write_arc_holds_readers_back_and_once_it_waited_long_newcomer_writers() {
let lock = Arc::new(RwLock::new(()));
let reader = ready(lock.read_arc());
let mut waiter = Box::pin(lock.write_arc());
assert!(poll_once(&mut waiter).is_pending());
let mut late_reader = Box::pin(lock.read_arc());
assert!(poll_once(&mut late_reader).is_pending());
assert!(lock.try_read_arc().is_none());
drop(late_reader);
thread::sleep(PATIENCE);
drop(reader);
let barging = lock
.try_write_arc()
.expect("a free lock is taken, whoever waits for it");
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert!(lock.try_write_arc().is_none());
let mut newcomer = Box::pin(lock.write_arc());
assert!(poll_once(&mut newcomer).is_pending());
let Poll::Ready(guard) = poll_once(&mut waiter) else {
panic!("the release of the barging guard lets the starved writer in");
};
drop(guard);
assert!(poll_once(&mut newcomer).is_ready());
}
#[test]
fn a_panic_while_a_mutex_guard_is_held_releases_the_mutex() {
let mutex = Mutex::new(1);
let mut guard = ready(mutex.lock());
let panicked = catch_unwind(AssertUnwindSafe(move || {
*guard = 2;
panic!("a panic while the guard is held");
}));
assert!(panicked.is_err());
assert_eq!(*mutex.try_lock().expect("the panic released the mutex"), 2);
}
#[test]
fn a_panic_while_an_rwlock_guard_is_held_releases_the_lock() {
let lock = RwLock::new(1);
let mut guard = ready(lock.write());
let panicked = catch_unwind(AssertUnwindSafe(move || {
*guard = 2;
panic!("a panic while the write guard is held");
}));
assert!(panicked.is_err());
assert_eq!(*lock.try_read().expect("the panic released the lock"), 2);
let guard = ready(lock.read());
let panicked = catch_unwind(AssertUnwindSafe(move || {
let _guard = guard;
panic!("a panic while a read guard is held");
}));
assert!(panicked.is_err());
*lock.try_write().expect("the panic released the lock") = 3;
assert_eq!(*lock.try_read().expect("the writer is gone"), 3);
}
#[test]
fn get_mut_reaches_the_value_without_a_guard() {
let mut mutex = Mutex::new(vec![1]);
mutex.get_mut().push(2);
assert_eq!(*ready(mutex.lock()), [1, 2]);
let mut lock = RwLock::new(vec![1]);
lock.get_mut().push(2);
assert_eq!(*ready(lock.read()), [1, 2]);
}
#[test]
fn into_inner_hands_back_the_value() {
let mutex = Mutex::new(vec![1]);
ready(mutex.lock()).push(2);
assert_eq!(mutex.into_inner(), [1, 2]);
let lock = RwLock::new(vec![1]);
ready(lock.write()).push(2);
assert_eq!(lock.into_inner(), [1, 2]);
}
#[test]
fn a_lock_is_made_from_a_default_or_from_a_value() {
let mutex = Mutex::<Vec<u8>>::default();
assert!(mutex.try_lock().expect("a new mutex is free").is_empty());
let mutex = Mutex::from(7);
assert_eq!(*mutex.try_lock().expect("a new mutex is free"), 7);
let lock = RwLock::<Vec<u8>>::default();
assert!(lock.try_write().expect("a new lock is free").is_empty());
let lock = RwLock::from(7);
assert_eq!(*lock.try_write().expect("a new lock is free"), 7);
}
#[test]
fn a_lock_prints_its_value_while_free_and_a_placeholder_while_held() {
let mutex = Mutex::new(1);
assert_eq!(format!("{mutex:?}"), "Mutex { value: 1 }");
let guard = ready(mutex.lock());
assert_eq!(format!("{mutex:?}"), "Mutex { value: <locked> }");
drop(guard);
assert_eq!(format!("{mutex:?}"), "Mutex { value: 1 }");
let holder = ready(mutex.lock());
let mut waiter = Box::pin(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = ready(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert_eq!(format!("{mutex:?}"), "Mutex { value: <locked> }");
drop(waiter);
assert_eq!(format!("{mutex:?}"), "Mutex { value: 1 }");
let lock = RwLock::new(1);
assert_eq!(format!("{lock:?}"), "RwLock { value: 1 }");
let writer = ready(lock.write());
assert_eq!(format!("{lock:?}"), "RwLock { value: <locked> }");
drop(writer);
let reader = ready(lock.read());
assert_eq!(format!("{lock:?}"), "RwLock { value: 1 }");
let mut waiting = Box::pin(lock.write());
assert!(poll_once(&mut waiting).is_pending());
assert_eq!(format!("{lock:?}"), "RwLock { value: <locked> }");
drop(waiting);
assert_eq!(format!("{lock:?}"), "RwLock { value: 1 }");
drop(reader);
}
#[test]
fn a_guard_prints_as_its_value() {
let mutex = Mutex::new(1);
let guard = ready(mutex.lock());
assert_eq!(format!("{guard:?}"), "1");
assert_eq!(format!("{guard:>3?}"), " 1");
drop(guard);
let lock = RwLock::new(vec![1, 2]);
let reader = ready(lock.read());
assert_eq!(format!("{reader:?}"), "[1, 2]");
assert_eq!(format!("{reader:#?}"), "[\n 1,\n 2,\n]");
drop(reader);
let writer = ready(lock.write());
assert_eq!(format!("{writer:?}"), "[1, 2]");
assert_eq!(format!("{writer:#?}"), "[\n 1,\n 2,\n]");
}
#[test]
fn an_arc_guard_holds_a_clone_of_the_arc_until_dropped() {
let mutex = Arc::new(Mutex::new(vec![1]));
let guard = ready(mutex.lock_arc());
assert_eq!(Arc::strong_count(&mutex), 2);
drop(guard);
assert_eq!(Arc::strong_count(&mutex), 1);
let mut guard = mutex.try_lock_arc().expect("a released mutex is free");
assert_eq!(Arc::strong_count(&mutex), 2);
drop(mutex);
guard.push(2);
assert_eq!(*guard, [1, 2]);
let lock = Arc::new(RwLock::new(vec![1]));
let first = ready(lock.read_arc());
let second = lock.try_read_arc().expect("readers share the lock");
assert_eq!(Arc::strong_count(&lock), 3);
drop(first);
drop(second);
assert_eq!(Arc::strong_count(&lock), 1);
let writer = ready(lock.write_arc());
assert_eq!(Arc::strong_count(&lock), 2);
drop(writer);
let mut writer = lock.try_write_arc().expect("the writer is gone");
assert_eq!(Arc::strong_count(&lock), 2);
drop(lock);
writer.push(2);
assert_eq!(*writer, [1, 2]);
}
#[test]
fn arc_guards_reach_an_unsized_value() {
let mutex: Arc<Mutex<[u8]>> = Arc::new(Mutex::new([1, 2, 3]));
ready(mutex.lock_arc())[0] = 4;
assert_eq!(
*mutex.try_lock_arc().expect("a released mutex is free"),
[4, 2, 3]
);
let lock: Arc<RwLock<dyn fmt::Debug + Send + Sync>> = Arc::new(RwLock::new(5u8));
assert_eq!(format!("{:?}", &*ready(lock.read_arc())), "5");
assert_eq!(format!("{:?}", &*ready(lock.write_arc())), "5");
}
#[test]
fn an_arc_guard_prints_as_its_value() {
let mutex = Arc::new(Mutex::new(1));
let guard = ready(mutex.lock_arc());
assert_eq!(format!("{guard:?}"), "1");
assert_eq!(format!("{guard:>3?}"), " 1");
drop(guard);
let lock = Arc::new(RwLock::new(vec![1, 2]));
let reader = ready(lock.read_arc());
assert_eq!(format!("{reader:?}"), "[1, 2]");
assert_eq!(format!("{reader:#?}"), "[\n 1,\n 2,\n]");
drop(reader);
let writer = ready(lock.write_arc());
assert_eq!(format!("{writer:?}"), "[1, 2]");
assert_eq!(format!("{writer:#?}"), "[\n 1,\n 2,\n]");
}
#[test]
fn a_mutex_coerces_to_an_unsized_value() {
let mutex: Arc<Mutex<dyn fmt::Debug + Send>> = Arc::new(Mutex::new(5u8));
let guard = ready(mutex.lock());
assert_eq!(format!("{:?}", &*guard), "5");
}
#[test]
fn a_rwlock_coerces_to_an_unsized_value() {
let lock: Arc<RwLock<dyn fmt::Debug + Send + Sync>> = Arc::new(RwLock::new(5u8));
let guard = ready(lock.read());
assert_eq!(format!("{:?}", &*guard), "5");
}
#[test]
#[timeout(15000)]
fn a_mutex_admits_one_holder_at_a_time_across_threads() {
let (threads, rounds) = if cfg!(miri) { (3, 20) } else { (8, 1000) };
let mutex = Arc::new(Mutex::new(0usize));
let start = Arc::new(Barrier::new(threads));
let takers: Vec<_> = (0..threads)
.map(|_| {
let mutex = mutex.clone();
let start = start.clone();
thread::spawn(move || {
start.wait();
for _ in 0..rounds {
let mut count = block_on(mutex.lock());
let seen = *count;
linger();
*count = seen + 1;
}
})
})
.collect();
for taker in takers {
taker.join().unwrap();
}
assert_eq!(*block_on(mutex.lock()), threads * rounds);
}
#[test]
#[timeout(15000)]
fn readers_never_see_a_pair_half_updated_by_a_writer() {
let (writers, readers, rounds) = if cfg!(miri) { (2, 2, 10) } else { (4, 4, 500) };
let lock = Arc::new(RwLock::new((0usize, 0usize)));
let start = Arc::new(Barrier::new(writers + readers));
let writer_threads: Vec<_> = (0..writers)
.map(|_| {
let lock = lock.clone();
let start = start.clone();
thread::spawn(move || {
start.wait();
for _ in 0..rounds {
let mut guard = block_on(lock.write());
guard.0 += 1;
linger();
guard.1 += 1;
}
})
})
.collect();
let reader_threads: Vec<_> = (0..readers)
.map(|_| {
let lock = lock.clone();
let start = start.clone();
thread::spawn(move || {
start.wait();
for _ in 0..rounds {
let guard = block_on(lock.read());
assert_eq!(guard.0, guard.1);
}
})
})
.collect();
for thread in writer_threads.into_iter().chain(reader_threads) {
thread.join().unwrap();
}
assert_eq!(*block_on(lock.read()), (writers * rounds, writers * rounds));
}
#[test]
#[timeout(15000)]
fn a_mutex_that_two_threads_keep_taking_still_lets_a_third_in() {
let mutex = Arc::new(Mutex::new(()));
let stop = Arc::new(AtomicBool::new(false));
let start = Arc::new(Barrier::new(3));
let takers: Vec<_> = (0..2)
.map(|_| {
let mutex = mutex.clone();
let stop = stop.clone();
let start = start.clone();
thread::spawn(move || {
start.wait();
while !stop.load(Ordering::Relaxed) {
let guard = block_on(mutex.lock());
linger();
drop(guard);
}
})
})
.collect();
let waiter = thread::spawn({
let mutex = mutex.clone();
let stop = stop.clone();
move || {
start.wait();
let guard = block_on(mutex.lock());
stop.store(true, Ordering::Relaxed);
drop(guard);
}
});
waiter.join().unwrap();
for taker in takers {
taker.join().unwrap();
}
assert!(mutex.try_lock().is_some());
}
#[test]
#[timeout(15000)]
fn an_rwlock_that_two_writers_keep_taking_still_lets_a_reader_in() {
let lock = Arc::new(RwLock::new(()));
let stop = Arc::new(AtomicBool::new(false));
let start = Arc::new(Barrier::new(3));
let writers: Vec<_> = (0..2)
.map(|_| {
let lock = lock.clone();
let stop = stop.clone();
let start = start.clone();
thread::spawn(move || {
start.wait();
while !stop.load(Ordering::Relaxed) {
let guard = block_on(lock.write());
linger();
drop(guard);
}
})
})
.collect();
let reader = thread::spawn({
let lock = lock.clone();
let stop = stop.clone();
move || {
start.wait();
let guard = block_on(lock.read());
stop.store(true, Ordering::Relaxed);
drop(guard);
}
});
reader.join().unwrap();
for writer in writers {
writer.join().unwrap();
}
assert!(lock.try_write().is_some());
assert!(lock.try_read().is_some());
}
#[test]
#[timeout(15000)]
fn a_release_racing_a_lock_is_not_missed() {
let rounds = if cfg!(miri) { 20 } else { 20_000 };
handoff(
Arc::new(Mutex::new(())),
rounds,
|mutex, start_waiting| {
let _guard = mutex
.try_lock()
.expect("nothing holds the mutex between rounds");
start_waiting();
},
|mutex| drop(block_on(mutex.lock())),
);
}
#[test]
#[timeout(15000)]
fn a_write_release_racing_a_read_is_not_missed() {
let rounds = if cfg!(miri) { 20 } else { 20_000 };
handoff(
Arc::new(RwLock::new(())),
rounds,
|lock, start_waiting| {
let _guard = lock
.try_write()
.expect("nothing holds the lock between rounds");
start_waiting();
},
|lock| drop(block_on(lock.read())),
);
}
#[test]
#[timeout(15000)]
fn a_read_release_racing_a_write_is_not_missed() {
let rounds = if cfg!(miri) { 20 } else { 20_000 };
handoff(
Arc::new(RwLock::new(())),
rounds,
|lock, start_waiting| {
let _guard = lock
.try_read()
.expect("nothing holds the lock between rounds");
start_waiting();
},
|lock| drop(block_on(lock.write())),
);
}
#[test]
#[timeout(15000)]
fn a_starved_lock_future_dropped_racing_a_lock_does_not_strand_it() {
let rounds = if cfg!(miri) { 20 } else { 2_000 };
handoff(
Arc::new(Mutex::new(())),
rounds,
|mutex, start_waiting| {
let starved = starved_lock(mutex);
start_waiting();
drop(starved);
},
|mutex| drop(block_on(mutex.lock())),
);
}
#[test]
#[timeout(15000)]
fn a_starved_write_future_dropped_racing_a_write_does_not_strand_it() {
let rounds = if cfg!(miri) { 20 } else { 2_000 };
handoff(
Arc::new(RwLock::new(())),
rounds,
|lock, start_waiting| {
let starved = starved_write(lock);
start_waiting();
drop(starved);
},
|lock| drop(block_on(lock.write())),
);
}
#[test]
#[timeout(15000)]
fn wakers_coming_back_into_the_lock_from_many_threads_do_not_deadlock() {
let (threads, rounds) = if cfg!(miri) { (3, 10) } else { (4, 250) };
let mutex = Arc::new(Mutex::new(0usize));
let start = Arc::new(Barrier::new(threads));
let takers: Vec<_> = (0..threads)
.map(|_| {
let mutex = mutex.clone();
let start = start.clone();
thread::spawn(move || {
start.wait();
for _ in 0..rounds {
let mut count = block_on(ComingBack {
mutex: mutex.clone(),
future: pin!(mutex.lock()),
});
let seen = *count;
linger();
*count = seen + 1;
}
})
})
.collect();
for taker in takers {
taker.join().unwrap();
}
assert_eq!(*block_on(mutex.lock()), threads * rounds);
}
fn poll_once<F>(future: &mut F) -> Poll<F::Output>
where
F: Future + Unpin,
{
pin!(future).poll(&mut Context::from_waker(Waker::noop()))
}
fn ready<F>(future: F) -> F::Output
where
F: Future,
{
let Poll::Ready(output) = pin!(future).poll(&mut Context::from_waker(Waker::noop())) else {
panic!("the future is pending, where it was expected to complete at once");
};
output
}
fn poll_with<F>(future: &mut F, waker: &Waker) -> Poll<F::Output>
where
F: Future + Unpin,
{
Pin::new(future).poll(&mut Context::from_waker(waker))
}
fn counting_waker() -> (Arc<AtomicUsize>, Waker) {
let woken = Arc::new(AtomicUsize::new(0));
let waker = Waker::from(Arc::new(CountWakes(woken.clone())));
(woken, waker)
}
fn starved_lock(mutex: &Mutex<()>) -> Pin<Box<impl Future<Output = MutexGuard<'_, ()>>>> {
let holder = mutex
.try_lock()
.expect("nothing holds the mutex between rounds");
let mut waiter = Box::pin(mutex.lock());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = mutex.try_lock().expect("the release left the mutex free");
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert!(
mutex.try_lock().is_none(),
"the waiter holds newcomers back"
);
waiter
}
fn starved_write(lock: &RwLock<()>) -> Pin<Box<impl Future<Output = RwLockWriteGuard<'_, ()>>>> {
let holder = lock
.try_write()
.expect("nothing holds the lock between rounds");
let mut waiter = Box::pin(lock.write());
assert!(poll_once(&mut waiter).is_pending());
thread::sleep(PATIENCE);
drop(holder);
let barging = lock.try_write().expect("the release left the lock free");
assert!(poll_once(&mut waiter).is_pending());
drop(barging);
assert!(
lock.try_write().is_none(),
"the waiter holds newcomers back"
);
waiter
}
fn linger() {
for _ in 0..64 {
hint::spin_loop();
}
}
fn handoff<L, H, W>(lock: Arc<L>, rounds: usize, hold: H, wait: W)
where
L: Send + Sync + 'static,
H: Fn(&L, &dyn Fn()),
W: Fn(&L) + Send + 'static,
{
let barrier = Arc::new(Barrier::new(2));
let waiter = thread::spawn({
let lock = lock.clone();
let barrier = barrier.clone();
move || {
for _ in 0..rounds {
barrier.wait();
wait(&lock);
barrier.wait();
}
}
});
for round in 0..rounds {
hold(&lock, &|| {
barrier.wait();
for _ in 0..round % 256 {
hint::spin_loop();
}
});
barrier.wait();
}
waiter.join().unwrap();
}
struct ComingBack<F> {
mutex: Arc<Mutex<usize>>,
future: F,
}
impl<F> Future for ComingBack<F>
where
F: Future + Unpin,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
let this = self.get_mut();
let waker = Waker::from(Arc::new(ComeBackThenWake {
mutex: this.mutex.clone(),
task: cx.waker().clone(),
}));
Pin::new(&mut this.future).poll(&mut Context::from_waker(&waker))
}
}
struct ComeBackThenWake {
mutex: Arc<Mutex<usize>>,
task: Waker,
}
impl Wake for ComeBackThenWake {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
drop(pin!(self.mutex.lock()).poll(&mut Context::from_waker(Waker::noop())));
self.task.wake_by_ref();
}
}
struct CountWakes(Arc<AtomicUsize>);
impl Wake for CountWakes {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}