use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::{Wake, Waker};
use super::{After, Side, Unwound, WAITING, WakerSet};
struct Count(AtomicUsize);
impl Wake for Count {
fn wake(self: Arc<Self>) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
fn counted() -> (Arc<Count>, Waker) {
let count = Arc::new(Count(AtomicUsize::new(0)));
let waker = Waker::from(Arc::clone(&count));
(count, waker)
}
#[test]
fn every_task_is_woken_even_past_the_slots() {
let set = WakerSet::<4>::new();
let tasks: Vec<_> = (0..6).map(|_| counted()).collect();
for (_, waker) in &tasks {
assert!(set.register(waker));
}
let mut after = After::default();
set.take_all(&mut after);
after.run();
for (i, (count, _)) in tasks.iter().enumerate() {
assert!(
count.0.load(Ordering::SeqCst) >= 1,
"task {i} was never woken"
);
}
}
#[test]
fn a_task_registering_again_keeps_its_slot() {
let set = WakerSet::<4>::new();
let (count, waker) = counted();
for _ in 0..10 {
assert!(set.register(&waker));
}
let mut after = After::default();
set.take_all(&mut after);
after.run();
assert_eq!(count.0.load(Ordering::SeqCst), 1);
let mut after = After::default();
set.take_all(&mut after);
after.run();
assert_eq!(count.0.load(Ordering::SeqCst), 1);
}
fn contended_side() -> (Arc<Count>, Side<()>) {
let side = Side::new(());
let (count, waker) = counted();
assert!(side.contended().register(&waker));
side.state.fetch_or(WAITING, Ordering::SeqCst);
(count, side)
}
#[test]
fn a_guard_dropped_normally_wakes_at_once() {
let (count, side) = contended_side();
drop(side.try_hold().expect("the side is free"));
assert_eq!(count.0.load(Ordering::SeqCst), 1);
}
#[test]
fn a_guard_dropped_by_unwinding_parks_its_wakes_for_the_draining_guard() {
use std::cell::Cell;
struct View<'a>(&'a Count, &'a Cell<Option<usize>>);
impl Drop for View<'_> {
fn drop(&mut self) {
self.1.set(Some(self.0.0.load(Ordering::SeqCst)));
}
}
let (count, side) = contended_side();
let at_view_drop = Cell::new(None);
let unwound = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _unwound = Unwound::enter();
let _view = View(&count, &at_view_drop);
let _held = side.try_hold().expect("the side is free");
panic!("a panic under the guard");
}));
assert!(unwound.is_err());
assert_eq!(at_view_drop.get(), Some(0), "woken while the view was live");
assert_eq!(
count.0.load(Ordering::SeqCst),
1,
"the draining guard ran the wake"
);
}
#[test]
fn unwinding_outside_a_view_wakes_at_once() {
let (count, side) = contended_side();
let at_catch = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _held = side.try_hold().expect("the side is free");
panic!("a panic under the guard, no view");
}));
assert!(at_catch.is_err());
assert_eq!(count.0.load(Ordering::SeqCst), 1);
super::run_unwound();
assert_eq!(count.0.load(Ordering::SeqCst), 1, "nothing was parked");
}