use crate::sync::loom_model::fair_handoff_counts;
use super::*;
const GRANTED_THEN_DROPPED: usize = 0;
const WITHDREW_AT_GRANT: usize = 1;
const RETURNED_TO_WORD: usize = 2;
fn explore_handoffs(
name: &str,
preemptions: usize,
min_interleavings: usize,
places: &[usize],
model: impl Fn() + Sync + Send + 'static,
) {
fair_handoff_counts();
explore(name, preemptions, min_interleavings, model);
let counts = fair_handoff_counts();
println!(
"loom model {name}: shares handed on {} times by a wait granted then dropped, {} by a \
waiter that withdrew at its grant, {} back to the free count",
counts[GRANTED_THEN_DROPPED], counts[WITHDREW_AT_GRANT], counts[RETURNED_TO_WORD]
);
for &place in places {
assert!(
counts[place] > 0,
"{name}: the search never handed a share on at place {place}"
);
}
}
pub(super) fn poll_once<F: Future + Unpin>(future: &mut F) -> Poll<F::Output> {
std::pin::Pin::new(future).poll(&mut Context::from_waker(std::task::Waker::noop()))
}
#[test]
fn a_reservation_dropped_racing_its_grant_passes_the_slot_on() {
explore_handoffs(
"a_reservation_dropped_racing_its_grant_passes_the_slot_on",
3,
200,
&[GRANTED_THEN_DROPPED, WITHDREW_AT_GRANT],
|| {
let (tx, rx) = mpsc::channel::<u8>(1);
let permit = tx.clone().try_reserve_owned().expect("the slot is free");
let mut first = Box::pin(tx.reserve());
let mut second = Box::pin(tx.reserve());
assert!(poll_once(&mut first).is_pending());
assert!(poll_once(&mut second).is_pending());
let releaser = thread::spawn(move || drop(permit));
drop(first);
let served = block_on(second).expect("the receiver is alive");
releaser.join().expect("releaser");
drop(served);
assert_eq!(tx.capacity(), 1, "a slot was lost or made up");
drop(rx);
},
);
}
#[test]
fn a_waiting_reserve_many_dropped_racing_its_grant_passes_its_share_on() {
explore_handoffs(
"a_waiting_reserve_many_dropped_racing_its_grant_passes_its_share_on",
3,
200,
&[GRANTED_THEN_DROPPED, WITHDREW_AT_GRANT, RETURNED_TO_WORD],
|| {
let (tx, rx) = mpsc::channel::<u8>(2);
let held = tx
.clone()
.try_reserve_owned()
.expect("the first slot is free");
let last = tx
.clone()
.try_reserve_owned()
.expect("the second slot is free");
let mut many = Box::pin(tx.reserve_many(2));
let mut one = Box::pin(tx.reserve());
assert!(poll_once(&mut many).is_pending());
assert!(poll_once(&mut one).is_pending());
drop(held);
assert_eq!(
tx.capacity(),
0,
"the first slot is set aside for the request"
);
let releaser = thread::spawn(move || drop(last));
drop(many);
let served = block_on(one).expect("the receiver is alive");
releaser.join().expect("releaser");
drop(served);
assert_eq!(tx.capacity(), 2, "a slot was lost or made up");
drop(rx);
},
);
}
#[test]
fn a_release_racing_two_waiting_reservations_serves_the_older_one() {
explore(
"a_release_racing_two_waiting_reservations_serves_the_older_one",
3,
100,
|| {
let (tx, rx) = mpsc::channel::<u8>(1);
let permit = tx.clone().try_reserve_owned().expect("the slot is free");
let mut older = Box::pin(tx.reserve());
let mut later = Box::pin(tx.reserve());
assert!(poll_once(&mut older).is_pending());
assert!(poll_once(&mut later).is_pending());
let releaser = thread::spawn(move || drop(permit));
let first = block_on(older).expect("the receiver is alive");
assert!(
poll_once(&mut later).is_pending(),
"the later reservation got a slot the older one holds"
);
releaser.join().expect("releaser");
drop(first);
let second = block_on(later).expect("the receiver is alive");
drop(second);
assert_eq!(tx.capacity(), 1);
drop(rx);
},
);
}
#[test]
fn a_close_racing_a_grant_ends_the_wait_once_and_returns_every_slot() {
let granted = std::sync::Arc::new(StdAtomicUsize::new(0));
let failed = std::sync::Arc::new(StdAtomicUsize::new(0));
let outcomes = (
std::sync::Arc::clone(&granted),
std::sync::Arc::clone(&failed),
);
explore_handoffs(
"a_close_racing_a_grant_ends_the_wait_once_and_returns_every_slot",
3,
200,
&[RETURNED_TO_WORD],
move || {
let (tx, mut rx) = mpsc::channel::<u8>(2);
let held = tx
.clone()
.try_reserve_owned()
.expect("the first slot is free");
let last = tx
.clone()
.try_reserve_owned()
.expect("the second slot is free");
let mut many = Box::pin(tx.reserve_many(2));
assert!(poll_once(&mut many).is_pending());
drop(held);
let releaser = thread::spawn(move || drop(last));
rx.close();
releaser.join().expect("releaser");
let sent = match poll_once(&mut many) {
Poll::Ready(Ok(permits)) => {
for (permit, value) in permits.zip(1..) {
permit.send(value);
}
outcomes.0.fetch_add(1, StdOrdering::Relaxed);
vec![1, 2]
}
Poll::Ready(Err(SendError(()))) => {
outcomes.1.fetch_add(1, StdOrdering::Relaxed);
vec![]
}
Poll::Pending => panic!("the request was neither granted nor failed"),
};
drop(many);
let mut received = Vec::new();
while let Some(value) = block_on(rx.recv()) {
received.push(value);
}
assert_eq!(received, sent);
assert_eq!(tx.capacity(), 2, "a slot was lost or made up");
},
);
assert!(
granted.load(StdOrdering::Relaxed) > 0,
"the grant never won"
);
assert!(failed.load(StdOrdering::Relaxed) > 0, "the close never won");
}