use std::cell::Cell;
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::rc::Rc;
use std::task::Waker;
struct Entry<K> {
key: K,
seq: u64,
waker: Waker,
canceled: Rc<Cell<bool>>,
granted: Rc<Cell<bool>>,
}
impl<K: Ord> Ord for Entry<K> {
fn cmp(&self, other: &Self) -> Ordering {
other.key.cmp(&self.key).then(other.seq.cmp(&self.seq))
}
}
impl<K: Ord> PartialOrd for Entry<K> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<K: Ord> PartialEq for Entry<K> {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && self.seq == other.seq
}
}
impl<K: Ord> Eq for Entry<K> {}
pub(crate) struct WaitQueue<K: Ord> {
capacity: usize,
in_use: usize,
next_seq: u64,
waiters: BinaryHeap<Entry<K>>,
}
impl<K: Ord> WaitQueue<K> {
pub(crate) fn new(capacity: usize) -> Self {
WaitQueue {
capacity,
in_use: 0,
next_seq: 0,
waiters: BinaryHeap::new(),
}
}
pub(crate) fn in_use(&self) -> usize {
self.in_use
}
pub(crate) fn capacity(&self) -> usize {
self.capacity
}
pub(crate) fn live_waiters(&self) -> usize {
self.waiters.iter().filter(|e| !e.canceled.get()).count()
}
pub(crate) fn try_acquire(&mut self) -> bool {
if self.in_use < self.capacity {
self.in_use += 1;
true
} else {
false
}
}
pub(crate) fn register(
&mut self,
key: K,
waker: Waker,
canceled: Rc<Cell<bool>>,
granted: Rc<Cell<bool>>,
) {
let seq = self.next_seq;
self.next_seq += 1;
self.waiters.push(Entry {
key,
seq,
waker,
canceled,
granted,
});
}
pub(crate) fn release(&mut self) {
while let Some(entry) = self.waiters.pop() {
if !entry.canceled.get() {
entry.granted.set(true);
entry.waker.wake();
return; }
}
self.in_use -= 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::sync::Arc;
use std::task::Wake;
struct RecordingWaker {
order: Arc<AtomicUsize>,
slot: Arc<AtomicUsize>,
}
impl Wake for RecordingWaker {
fn wake(self: Arc<Self>) {
let tick = self.order.fetch_add(1, AtomicOrdering::SeqCst) + 1;
self.slot.store(tick, AtomicOrdering::SeqCst);
}
}
fn recording(order: &Arc<AtomicUsize>) -> (Waker, Arc<AtomicUsize>) {
let slot = Arc::new(AtomicUsize::new(0));
let waker = Waker::from(Arc::new(RecordingWaker {
order: Arc::clone(order),
slot: Arc::clone(&slot),
}));
(waker, slot)
}
fn live() -> Rc<Cell<bool>> {
Rc::new(Cell::new(false))
}
fn register_waiter<K: Ord>(
q: &mut WaitQueue<K>,
key: K,
order: &Arc<AtomicUsize>,
) -> (Arc<AtomicUsize>, Rc<Cell<bool>>) {
let (waker, slot) = recording(order);
let canceled = live();
let granted = live();
q.register(key, waker, Rc::clone(&canceled), Rc::clone(&granted));
(slot, granted)
}
#[test]
fn try_acquire_respects_capacity() {
let mut q: WaitQueue<()> = WaitQueue::new(2);
assert!(q.try_acquire());
assert!(q.try_acquire());
assert!(!q.try_acquire()); assert_eq!(q.in_use(), 2);
assert_eq!(q.capacity(), 2);
q.release();
assert_eq!(q.in_use(), 1);
assert!(q.try_acquire());
}
#[test]
fn release_hands_off_directly_without_freeing_the_unit() {
let mut q: WaitQueue<()> = WaitQueue::new(1);
assert!(q.try_acquire());
let order = Arc::new(AtomicUsize::new(0));
let (s0, g0) = register_waiter(&mut q, (), &order);
q.release();
assert!(g0.get(), "unit must be handed directly to the waiter");
assert_eq!(s0.load(AtomicOrdering::SeqCst), 1, "waiter must be woken");
assert_eq!(q.in_use(), 1, "unit must stay in use — never observably free");
assert!(!q.try_acquire(), "a fresh request must not be able to steal it");
}
#[test]
fn fifo_order_for_unit_key() {
let mut q: WaitQueue<()> = WaitQueue::new(1);
assert!(q.try_acquire());
let order = Arc::new(AtomicUsize::new(0));
let (s0, g0) = register_waiter(&mut q, (), &order);
let (s1, g1) = register_waiter(&mut q, (), &order);
let (s2, g2) = register_waiter(&mut q, (), &order);
q.release();
assert!(g0.get() && !g1.get() && !g2.get());
assert_eq!(q.in_use(), 1);
q.release();
assert!(g1.get() && !g2.get());
assert_eq!(q.in_use(), 1);
q.release();
assert!(g2.get());
assert_eq!(q.in_use(), 1);
q.release();
assert_eq!(q.in_use(), 0);
assert_eq!(s0.load(AtomicOrdering::SeqCst), 1);
assert_eq!(s1.load(AtomicOrdering::SeqCst), 2);
assert_eq!(s2.load(AtomicOrdering::SeqCst), 3);
}
#[test]
fn priority_order_then_fifo_within_level() {
let mut q: WaitQueue<u32> = WaitQueue::new(1);
assert!(q.try_acquire());
let order = Arc::new(AtomicUsize::new(0));
let (s_lo_a, _g_lo_a) = register_waiter(&mut q, 5, &order); let (s_hi, _g_hi) = register_waiter(&mut q, 1, &order); let (s_lo_b, _g_lo_b) = register_waiter(&mut q, 5, &order);
q.release(); q.release(); q.release(); assert_eq!(s_hi.load(AtomicOrdering::SeqCst), 1);
assert_eq!(s_lo_a.load(AtomicOrdering::SeqCst), 2);
assert_eq!(s_lo_b.load(AtomicOrdering::SeqCst), 3);
}
#[test]
fn release_skips_canceled_waiter() {
let mut q: WaitQueue<u32> = WaitQueue::new(1);
assert!(q.try_acquire());
let order = Arc::new(AtomicUsize::new(0));
let (w_dead, s_dead) = recording(&order);
let dead_flag = live();
let dead_granted = live();
q.register(0, w_dead, Rc::clone(&dead_flag), Rc::clone(&dead_granted)); let (s_live, g_live) = register_waiter(&mut q, 1, &order);
dead_flag.set(true);
q.release();
assert_eq!(s_dead.load(AtomicOrdering::SeqCst), 0);
assert!(!dead_granted.get());
assert_eq!(s_live.load(AtomicOrdering::SeqCst), 1);
assert!(g_live.get());
assert_eq!(q.in_use(), 1); }
#[test]
fn release_with_only_canceled_waiters_frees_the_unit() {
let mut q: WaitQueue<()> = WaitQueue::new(1);
assert!(q.try_acquire());
let order = Arc::new(AtomicUsize::new(0));
let (w, s) = recording(&order);
let flag = live();
let granted = live();
q.register((), w, Rc::clone(&flag), Rc::clone(&granted));
flag.set(true);
q.release();
assert_eq!(s.load(AtomicOrdering::SeqCst), 0); assert!(!granted.get()); assert_eq!(q.in_use(), 0); }
}