use slab::Slab;
#[derive(Debug)]
pub(crate) struct WaitList<T> {
sentinel: Option<usize>,
nodes: Slab<Node<T>>,
}
#[derive(Debug)]
struct Node<T> {
prev: usize,
next: usize,
value: Option<T>,
}
impl<T> WaitList<T> {
fn ensure_init(&mut self) -> usize {
if let Some(sentinel) = self.sentinel {
return sentinel;
}
let first = self.nodes.vacant_entry();
let sentinel = first.key();
first.insert(Node {
prev: sentinel,
next: sentinel,
value: None,
});
self.sentinel = Some(sentinel);
sentinel
}
pub(crate) const fn new() -> Self {
Self {
sentinel: None,
nodes: Slab::new(),
}
}
pub(crate) fn register_waiter_to_head(
&mut self,
idx: &mut Option<usize>,
f: impl FnOnce() -> Option<T>,
) {
assert!(idx.is_none());
let sentinel = self.ensure_init();
let value = f();
let prev_head = self.nodes[sentinel].next;
let new_node = Node {
prev: sentinel,
next: prev_head,
value,
};
let new_key = self.nodes.insert(new_node);
self.nodes[sentinel].next = new_key;
self.nodes[prev_head].prev = new_key;
*idx = Some(new_key);
}
pub(crate) fn register_waiter_to_tail(
&mut self,
idx: &mut Option<usize>,
f: impl FnOnce() -> Option<T>,
) {
assert!(idx.is_none());
let sentinel = self.ensure_init();
let value = f();
let prev_tail = self.nodes[sentinel].prev;
let new_node = Node {
prev: prev_tail,
next: sentinel,
value,
};
let new_key = self.nodes.insert(new_node);
self.nodes[sentinel].prev = new_key;
self.nodes[prev_tail].next = new_key;
*idx = Some(new_key);
}
pub(crate) fn unlink_waiter(
&mut self,
idx: usize,
should_unlink: impl FnOnce(&mut T) -> bool,
) -> Option<&mut T> {
let sentinel = self.sentinel.expect("wait list must be initialized");
assert_ne!(idx, sentinel);
fn value_mut<T>(node: &mut Node<T>) -> &mut T {
node.value
.as_mut()
.expect("waiter node must contain a value")
}
if should_unlink(value_mut(&mut self.nodes[idx])) {
let prev = self.nodes[idx].prev;
let next = self.nodes[idx].next;
let is_unlinked = prev == idx;
assert_eq!(is_unlinked, next == idx, "waiter links must be consistent");
if !is_unlinked {
self.nodes[prev].next = next;
self.nodes[next].prev = prev;
self.nodes[idx].prev = idx;
self.nodes[idx].next = idx;
}
Some(value_mut(&mut self.nodes[idx]))
} else {
None
}
}
pub(crate) fn unlink_first_waiter(
&mut self,
should_unlink: impl FnOnce(&mut T) -> bool,
) -> Option<&mut T> {
let sentinel = self.sentinel?;
let first = self.nodes[sentinel].next;
if first != sentinel {
self.unlink_waiter(first, should_unlink)
} else {
None
}
}
pub(crate) fn is_empty(&self) -> bool {
self.sentinel
.is_none_or(|sentinel| self.nodes[sentinel].next == sentinel)
}
pub(crate) fn waiter_mut(&mut self, idx: usize) -> &mut T {
self.nodes[idx]
.value
.as_mut()
.expect("waiter node must contain a value")
}
pub(crate) fn remove_unlinked_waiter(&mut self, idx: usize) {
let node = &self.nodes[idx];
assert_eq!(node.prev, idx, "waiter must be unlinked before removal");
assert_eq!(node.next, idx, "waiter must be unlinked before removal");
self.nodes.remove(idx);
}
}