use std::marker::PhantomPinned;
use std::task::Waker;
pub(crate) struct WaiterNode {
pub(crate) waker: Option<Waker>,
pub(crate) next: *mut Self,
pub(crate) prev: *mut Self,
pub(crate) notified: bool,
_pinned: PhantomPinned,
}
impl WaiterNode {
pub(crate) fn new() -> Self {
Self {
waker: None,
next: std::ptr::null_mut(),
prev: std::ptr::null_mut(),
notified: false,
_pinned: PhantomPinned,
}
}
}
pub(crate) struct WaiterList {
head: *mut WaiterNode,
tail: *mut WaiterNode,
}
impl WaiterList {
pub(crate) fn new() -> Self {
Self {
head: std::ptr::null_mut(),
tail: std::ptr::null_mut(),
}
}
#[cfg(test)]
fn is_empty(&self) -> bool {
self.head.is_null()
}
pub(crate) unsafe fn push_back(&mut self, node: *mut WaiterNode) {
let node_mut = node;
unsafe {
(*node_mut).next = std::ptr::null_mut();
}
unsafe {
(*node_mut).prev = self.tail;
}
if self.tail.is_null() {
self.head = node;
} else {
unsafe {
(*self.tail).next = node;
}
}
self.tail = node;
}
pub(crate) unsafe fn remove(&mut self, node: *mut WaiterNode) {
let prev = unsafe { (*node).prev };
let next = unsafe { (*node).next };
if prev.is_null() {
self.head = next;
} else {
unsafe {
(*prev).next = next;
}
}
if next.is_null() {
self.tail = prev;
} else {
unsafe {
(*next).prev = prev;
}
}
let node_mut = node;
unsafe {
(*node_mut).next = std::ptr::null_mut();
}
unsafe {
(*node_mut).prev = std::ptr::null_mut();
}
}
pub(crate) unsafe fn pop_front(&mut self) -> Option<*mut WaiterNode> {
if self.head.is_null() {
return None;
}
let node = self.head;
let next = unsafe { (*node).next };
self.head = next;
if next.is_null() {
self.tail = std::ptr::null_mut();
} else {
unsafe {
(*next).prev = std::ptr::null_mut();
}
}
unsafe {
(*node).next = std::ptr::null_mut();
}
unsafe {
(*node).prev = std::ptr::null_mut();
}
Some(node)
}
#[cfg(test)]
pub(crate) unsafe fn for_each(&self, mut f: impl FnMut(*mut WaiterNode)) {
let mut current = self.head;
while !current.is_null() {
let next = unsafe { (*current).next };
f(current);
current = next;
}
}
pub(crate) fn head(&self) -> *mut WaiterNode {
self.head
}
}
unsafe impl Send for WaiterList {}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
#[allow(
clippy::undocumented_unsafe_blocks,
clippy::multiple_unsafe_ops_per_block,
clippy::indexing_slicing,
reason = "test-only code with trivial safety invariants"
)]
mod tests {
use super::*;
#[test]
fn push_back_and_pop_front_fifo_order() {
let mut list = WaiterList::new();
let mut a = WaiterNode::new();
let mut b = WaiterNode::new();
let mut c = WaiterNode::new();
let pa: *mut WaiterNode = &raw mut a;
let pb: *mut WaiterNode = &raw mut b;
let pc: *mut WaiterNode = &raw mut c;
unsafe {
list.push_back(pa);
list.push_back(pb);
list.push_back(pc);
}
assert!(!list.is_empty());
let first = unsafe { list.pop_front() };
assert!(std::ptr::eq(first.unwrap(), pa));
let second = unsafe { list.pop_front() };
assert!(std::ptr::eq(second.unwrap(), pb));
let third = unsafe { list.pop_front() };
assert!(std::ptr::eq(third.unwrap(), pc));
assert!(list.is_empty());
assert!(unsafe { list.pop_front() }.is_none());
}
#[test]
fn remove_middle_node() {
let mut list = WaiterList::new();
let mut a = WaiterNode::new();
let mut b = WaiterNode::new();
let mut c = WaiterNode::new();
let pa: *mut WaiterNode = &raw mut a;
let pb: *mut WaiterNode = &raw mut b;
let pc: *mut WaiterNode = &raw mut c;
unsafe {
list.push_back(pa);
list.push_back(pb);
list.push_back(pc);
list.remove(pb);
}
let first = unsafe { list.pop_front() };
assert!(std::ptr::eq(first.unwrap(), pa));
let second = unsafe { list.pop_front() };
assert!(std::ptr::eq(second.unwrap(), pc));
assert!(list.is_empty());
}
#[test]
fn remove_head_node() {
let mut list = WaiterList::new();
let mut a = WaiterNode::new();
let mut b = WaiterNode::new();
let pa: *mut WaiterNode = &raw mut a;
let pb: *mut WaiterNode = &raw mut b;
unsafe {
list.push_back(pa);
list.push_back(pb);
list.remove(pa);
}
let first = unsafe { list.pop_front() };
assert!(std::ptr::eq(first.unwrap(), pb));
assert!(list.is_empty());
}
#[test]
fn remove_tail_node() {
let mut list = WaiterList::new();
let mut a = WaiterNode::new();
let mut b = WaiterNode::new();
let pa: *mut WaiterNode = &raw mut a;
let pb: *mut WaiterNode = &raw mut b;
unsafe {
list.push_back(pa);
list.push_back(pb);
list.remove(pb);
}
let first = unsafe { list.pop_front() };
assert!(std::ptr::eq(first.unwrap(), pa));
assert!(list.is_empty());
}
#[test]
fn remove_only_node() {
let mut list = WaiterList::new();
let mut a = WaiterNode::new();
let pa: *mut WaiterNode = &raw mut a;
unsafe {
list.push_back(pa);
list.remove(pa);
}
assert!(list.is_empty());
}
#[test]
fn for_each_visits_all_nodes() {
let mut list = WaiterList::new();
let mut a = WaiterNode::new();
let mut b = WaiterNode::new();
let pa: *mut WaiterNode = &raw mut a;
let pb: *mut WaiterNode = &raw mut b;
unsafe {
list.push_back(pa);
list.push_back(pb);
}
let mut visited = Vec::new();
unsafe {
list.for_each(|node| visited.push(node));
}
assert_eq!(visited.len(), 2);
assert!(std::ptr::eq(visited[0], pa));
assert!(std::ptr::eq(visited[1], pb));
}
}