use super::*;
use crate::loom::thread;
use std::task::Context;
use futures_test::task::{new_count_waker, AwokenCount};
#[cfg(loom)]
const NUM_ITEMS: usize = 16;
#[cfg(not(loom))]
const NUM_ITEMS: usize = 64;
fn new_handle() -> (EntryHandle, AwokenCount) {
new_handle_with_deadline(0)
}
fn new_handle_with_deadline(deadline: u64) -> (EntryHandle, AwokenCount) {
let (waker, count) = new_count_waker();
let entry = EntryHandle::new(deadline);
_ = entry.poll(&mut Context::from_waker(&waker));
(entry, count)
}
fn model<F: Fn() + Send + Sync + 'static>(f: F) {
#[cfg(loom)]
loom::model(f);
#[cfg(not(loom))]
f();
}
#[test]
fn wake_up_in_the_same_thread() {
model(|| {
let mut counts = Vec::new();
let mut reg_queue = RegistrationQueue::new();
for _ in 0..NUM_ITEMS {
let (hdl, count) = new_handle();
counts.push(count);
unsafe {
reg_queue.push_front(hdl);
}
}
let mut wake_queue = WakeQueue::new();
for _ in 0..NUM_ITEMS {
if let Some(hdl) = reg_queue.pop_front() {
unsafe {
wake_queue.push_front(hdl);
}
}
}
assert!(reg_queue.pop_front().is_none());
wake_queue.wake_all();
assert!(counts.into_iter().all(|c| c.get() == 1));
});
}
#[test]
fn cancel_in_the_same_thread() {
model(|| {
let mut counts = Vec::new();
let (cancel_tx, mut cancel_rx) = cancellation_queue::new();
let mut reg_queue = RegistrationQueue::new();
for _ in 0..NUM_ITEMS {
let (hdl, count) = new_handle();
assert!(hdl.register_cancel_tx(cancel_tx.clone()));
counts.push(count);
unsafe {
reg_queue.push_front(hdl.clone());
}
hdl.cancel();
}
while let Some(hdl) = reg_queue.pop_front() {
drop(hdl);
}
let mut wake_queue = WakeQueue::new();
for hdl in cancel_rx.recv_all() {
unsafe {
wake_queue.push_front(hdl);
}
}
wake_queue.wake_all();
assert!(counts.into_iter().all(|c| c.get() == 0));
});
}
#[test]
fn insert_of_already_cancelled_entry_does_not_enter_wheel() {
model(|| {
let (cancel_tx, mut cancel_rx) = cancellation_queue::new();
let mut wheel = Wheel::new();
let far_future = 10_000_000;
let (hdl, awoken_count) = new_handle_with_deadline(far_future);
hdl.cancel();
assert!(hdl.is_cancelled());
unsafe {
wheel.insert(hdl, cancel_tx);
}
assert!(
wheel.next_expiration_time().is_none(),
"an already-cancelled entry leaked into the wheel on insert"
);
assert_eq!(cancel_rx.recv_all().count(), 0);
assert_eq!(awoken_count.get(), 0);
let mut wake_queue = WakeQueue::new();
wheel.take_expired(u64::MAX, &mut wake_queue);
wake_queue.wake_all();
});
}
#[test]
fn cancel_races_with_insert() {
model(|| {
let (cancel_tx, mut cancel_rx) = cancellation_queue::new();
let mut wheel = Wheel::new();
let far_future = 10_000_000;
let (hdl, count) = new_handle_with_deadline(far_future);
let hdl2 = hdl.clone();
let jh = thread::spawn(move || {
hdl2.cancel();
});
unsafe {
wheel.insert(hdl, cancel_tx);
}
jh.join().unwrap();
let cancelled_via_queue = cancel_rx.recv_all().count();
let leaked_in_wheel = wheel.next_expiration_time().is_some();
assert!(
!leaked_in_wheel || cancelled_via_queue == 1,
"entry is stuck in the wheel with no way to ever be removed"
);
assert_eq!(count.get(), 0, "a cancelled entry must never be woken");
let mut wake_queue = WakeQueue::new();
wheel.take_expired(u64::MAX, &mut wake_queue);
wake_queue.wake_all();
});
}
#[test]
fn wake_up_in_the_different_thread() {
model(|| {
let mut counts = Vec::new();
let mut hdls = Vec::new();
let mut reg_queue = RegistrationQueue::new();
for _ in 0..NUM_ITEMS {
let (hdl, count) = new_handle();
counts.push(count);
hdls.push(hdl.clone());
unsafe {
reg_queue.push_front(hdl);
}
}
thread::spawn(move || {
let mut wake_queue = WakeQueue::new();
for _ in 0..NUM_ITEMS {
if let Some(hdl) = reg_queue.pop_front() {
unsafe {
wake_queue.push_front(hdl);
}
}
}
assert!(reg_queue.pop_front().is_none());
wake_queue.wake_all();
assert!(counts.into_iter().all(|c| c.get() == 1));
})
.join()
.unwrap();
});
}
#[test]
fn cancel_in_the_different_thread() {
model(|| {
let mut counts = Vec::new();
let (cancel_tx, mut cancel_rx) = cancellation_queue::new();
let mut hdls = Vec::new();
let mut reg_queue = RegistrationQueue::new();
for _ in 0..NUM_ITEMS {
let (hdl, count) = new_handle();
assert!(hdl.register_cancel_tx(cancel_tx.clone()));
counts.push(count);
hdls.push(hdl.clone());
unsafe {
reg_queue.push_front(hdl);
}
}
let jh = thread::spawn(move || {
for hdl in hdls {
hdl.cancel();
}
});
while let Some(hdl) = reg_queue.pop_front() {
drop(hdl);
}
let mut wake_queue = WakeQueue::new();
for hdl in cancel_rx.recv_all() {
unsafe {
wake_queue.push_front(hdl);
}
}
wake_queue.wake_all();
assert!(counts.into_iter().all(|c| c.get() == 0));
jh.join().unwrap();
})
}