use std::collections::VecDeque;
use std::task::Waker;
#[cfg(loom)]
use loom::sync::atomic::{AtomicUsize, Ordering};
#[cfg(not(loom))]
use std::sync::atomic::{AtomicUsize, Ordering};
#[cfg(loom)]
use loom::sync::Mutex;
#[cfg(not(loom))]
use parking_lot::Mutex;
#[cfg(loom)]
type Guard<'a, T> = loom::sync::MutexGuard<'a, T>;
#[cfg(not(loom))]
type Guard<'a, T> = parking_lot::MutexGuard<'a, T>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Ticket {
slot: u32,
generation: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Closed;
struct Slot {
waker: Option<Waker>,
generation: u64,
}
struct Inner {
slots: Vec<Slot>,
free: Vec<u32>,
order: VecDeque<(u32, u64)>,
next_generation: u64,
closed: bool,
}
impl Inner {
fn live(&self, ticket: Ticket) -> bool {
self.slots
.get(ticket.slot as usize)
.is_some_and(|slot| slot.generation == ticket.generation && slot.waker.is_some())
}
fn retire(&mut self, slot: u32) -> Option<Waker> {
let entry = &mut self.slots[slot as usize];
let waker = entry.waker.take();
if waker.is_some() {
entry.generation = entry.generation.wrapping_add(1);
self.free.push(slot);
}
waker
}
fn compact(&mut self) {
if self.order.len() <= 8 || self.order.len() < self.free.len() * 2 {
return;
}
let slots = &self.slots;
self.order.retain(|&(slot, generation)| {
slots[slot as usize].generation == generation && slots[slot as usize].waker.is_some()
});
}
}
pub(crate) struct Waiters {
inner: Mutex<Inner>,
waiting: AtomicUsize,
}
impl Waiters {
pub(crate) fn new() -> Self {
Self {
inner: Mutex::new(Inner {
slots: Vec::new(),
free: Vec::new(),
order: VecDeque::new(),
next_generation: 0,
closed: false,
}),
waiting: AtomicUsize::new(0),
}
}
#[inline]
pub(crate) fn any(&self) -> bool {
self.waiting.load(Ordering::Relaxed) != 0
}
pub(crate) fn park(&self, waker: &Waker) -> Result<Ticket, Closed> {
let mut inner = self.lock();
if inner.closed {
return Err(Closed);
}
let generation = inner.next_generation;
inner.next_generation = generation.wrapping_add(1);
let slot = match inner.free.pop() {
Some(slot) => {
let entry = &mut inner.slots[slot as usize];
entry.waker = Some(waker.clone());
entry.generation = generation;
slot
}
None => {
let slot = u32::try_from(inner.slots.len())
.expect("a mailbox parks fewer than 2^32 senders at once");
inner.slots.push(Slot { waker: Some(waker.clone()), generation });
slot
}
};
inner.order.push_back((slot, generation));
inner.compact();
self.publish(&inner);
Ok(Ticket { slot, generation })
}
pub(crate) fn refresh(&self, ticket: Ticket, waker: &Waker) {
let mut inner = self.lock();
if !inner.live(ticket) {
return;
}
let entry = &mut inner.slots[ticket.slot as usize];
match &entry.waker {
Some(existing) if existing.will_wake(waker) => {}
_ => entry.waker = Some(waker.clone()),
}
}
pub(crate) fn cancel(&self, ticket: Ticket) -> bool {
let mut inner = self.lock();
if !inner.live(ticket) {
return false;
}
drop(inner.retire(ticket.slot));
inner.compact();
self.publish(&inner);
true
}
pub(crate) fn wake_one(&self) -> bool {
let waker = {
let mut inner = self.lock();
let woken = loop {
let Some((slot, generation)) = inner.order.pop_front() else { break None };
if inner.slots[slot as usize].generation != generation {
continue;
}
if let Some(waker) = inner.retire(slot) {
break Some(waker);
}
};
self.publish(&inner);
woken
};
match waker {
Some(waker) => {
waker.wake();
true
}
None => false,
}
}
pub(crate) fn close(&self) {
let woken = {
let mut inner = self.lock();
inner.closed = true;
let mut woken = Vec::new();
while let Some((slot, generation)) = inner.order.pop_front() {
if inner.slots[slot as usize].generation != generation {
continue;
}
woken.extend(inner.retire(slot));
}
self.publish(&inner);
woken
};
for waker in woken {
waker.wake();
}
}
fn publish(&self, inner: &Inner) {
let live = inner.slots.len() - inner.free.len();
self.waiting.store(live, Ordering::Release);
}
fn lock(&self) -> Guard<'_, Inner> {
#[cfg(not(loom))]
{
self.inner.lock()
}
#[cfg(loom)]
{
self.inner.lock().unwrap()
}
}
}
impl std::fmt::Debug for Waiters {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Waiters").field("waiting", &self.waiting.load(Ordering::Relaxed)).finish()
}
}
#[cfg(all(test, not(loom)))]
mod tests {
use super::*;
use std::sync::Arc;
use std::task::Wake;
struct Sender {
id: usize,
woken: Arc<std::sync::Mutex<Vec<usize>>>,
}
impl Wake for Sender {
fn wake(self: Arc<Self>) {
self.woken.lock().unwrap().push(self.id);
}
}
struct Log(Arc<std::sync::Mutex<Vec<usize>>>);
impl Log {
fn new() -> Self {
Self(Arc::new(std::sync::Mutex::new(Vec::new())))
}
fn waker(&self, id: usize) -> Waker {
Waker::from(Arc::new(Sender { id, woken: Arc::clone(&self.0) }))
}
fn woken(&self) -> Vec<usize> {
self.0.lock().unwrap().clone()
}
}
#[test]
fn senders_are_let_back_in_in_the_order_they_arrived() {
let waiters = Waiters::new();
let log = Log::new();
for id in 0..4 {
waiters.park(&log.waker(id)).expect("the mailbox is open");
}
assert!(waiters.any());
for _ in 0..4 {
assert!(waiters.wake_one());
}
assert_eq!(log.woken(), [0, 1, 2, 3]);
assert!(!waiters.any(), "waking every sender empties the queue");
assert!(!waiters.wake_one(), "an empty queue has nobody to hand a slot to");
}
#[test]
fn a_woken_sender_is_not_woken_a_second_time() {
let waiters = Waiters::new();
let log = Log::new();
let first = waiters.park(&log.waker(0)).unwrap();
waiters.park(&log.waker(1)).unwrap();
waiters.wake_one();
assert!(!waiters.cancel(first), "a woken registration is already over");
waiters.wake_one();
assert_eq!(log.woken(), [0, 1]);
}
#[test]
fn a_cancelled_sender_is_skipped_and_the_rest_keep_their_order() {
let waiters = Waiters::new();
let log = Log::new();
let first = waiters.park(&log.waker(0)).unwrap();
let second = waiters.park(&log.waker(1)).unwrap();
waiters.park(&log.waker(2)).unwrap();
assert!(waiters.cancel(second), "the registration was live");
assert!(!waiters.cancel(second), "cancelling twice is harmless");
assert!(waiters.wake_one());
assert!(waiters.wake_one());
assert!(!waiters.wake_one());
assert_eq!(log.woken(), [0, 2], "the cancelled sender absorbed no wake");
assert!(!waiters.cancel(first));
}
#[test]
fn a_stale_ticket_cannot_cancel_whoever_inherited_its_slot() {
let waiters = Waiters::new();
let log = Log::new();
let first = waiters.park(&log.waker(0)).unwrap();
waiters.cancel(first);
let second = waiters.park(&log.waker(1)).unwrap();
assert_eq!(first.slot, second.slot, "the test is only meaningful if the slot is reused");
assert_ne!(first.generation, second.generation);
assert!(!waiters.cancel(first), "the stale ticket must do nothing");
assert!(waiters.wake_one());
assert_eq!(log.woken(), [1], "the live sender survived the stale cancel");
}
#[test]
fn re_polling_a_parked_sender_points_it_at_the_new_waker() {
let waiters = Waiters::new();
let log = Log::new();
let ticket = waiters.park(&log.waker(0)).unwrap();
waiters.refresh(ticket, &log.waker(1));
waiters.wake_one();
assert_eq!(log.woken(), [1]);
waiters.refresh(ticket, &log.waker(2));
assert!(!waiters.wake_one());
assert_eq!(log.woken(), [1]);
}
#[test]
fn closing_wakes_everyone_and_refuses_new_arrivals() {
let waiters = Waiters::new();
let log = Log::new();
for id in 0..3 {
waiters.park(&log.waker(id)).unwrap();
}
waiters.close();
assert_eq!(log.woken(), [0, 1, 2], "every parked sender is told to look again");
assert!(!waiters.any());
assert_eq!(waiters.park(&log.waker(9)), Err(Closed), "a closed mailbox parks nobody");
assert_eq!(log.woken(), [0, 1, 2]);
}
#[test]
fn slots_are_recycled_so_the_queue_tracks_peak_waiters_not_throughput() {
let waiters = Waiters::new();
let log = Log::new();
for round in 0..1_000 {
waiters.park(&log.waker(round)).unwrap();
assert!(waiters.wake_one());
}
assert_eq!(waiters.inner.lock().slots.len(), 1, "one sender at a time needs one slot");
assert_eq!(log.woken().len(), 1_000);
}
#[test]
fn a_burst_of_cancellations_does_not_grow_the_queue_without_bound() {
let waiters = Waiters::new();
let log = Log::new();
let held = waiters.park(&log.waker(0)).unwrap();
for round in 1..10_000 {
let ticket = waiters.park(&log.waker(round)).unwrap();
assert!(waiters.cancel(ticket));
}
assert!(
waiters.inner.lock().order.len() < 64,
"tombstones accumulated: {}",
waiters.inner.lock().order.len()
);
assert!(waiters.wake_one());
assert_eq!(log.woken(), [0]);
assert!(!waiters.cancel(held));
}
#[test]
fn the_debug_rendering_reports_how_many_are_parked() {
let waiters = Waiters::new();
let log = Log::new();
assert!(format!("{waiters:?}").contains("waiting: 0"));
waiters.park(&log.waker(0)).unwrap();
assert!(format!("{waiters:?}").contains("waiting: 1"));
}
}