use core::{
cell::Cell,
pin::Pin,
ptr::{self, NonNull},
sync::atomic::{AtomicPtr, Ordering},
};
use super::atomic_waker::AtomicWaker;
pub struct NodeHeader {
next: AtomicPtr<NodeHeader>,
}
impl NodeHeader {
pub fn new() -> NodeHeader {
NodeHeader {
next: AtomicPtr::new(ptr::null_mut()),
}
}
}
pub struct Queue {
waker: AtomicWaker,
head: AtomicPtr<NodeHeader>,
tail: Cell<*const NodeHeader>,
stub: NodeHeader,
}
unsafe impl Send for Queue {}
unsafe impl Sync for Queue {}
#[derive(Debug)]
pub enum Pop {
Empty,
Value(NonNull<NodeHeader>),
Inconsistant,
}
impl Queue {
pub fn new() -> Self {
Queue {
waker: AtomicWaker::new(),
head: AtomicPtr::new(ptr::null_mut()),
tail: Cell::new(ptr::null_mut()),
stub: NodeHeader {
next: AtomicPtr::new(ptr::null_mut()),
},
}
}
pub fn waker(&self) -> &AtomicWaker {
&self.waker
}
pub unsafe fn init(self: Pin<&Self>) {
let ptr = &self.stub as *const _ as *mut _;
self.head.store(ptr, Ordering::Release);
self.tail.set(ptr);
}
pub unsafe fn push(self: Pin<&Self>, node: NonNull<NodeHeader>) {
node.as_ref().next.store(ptr::null_mut(), Ordering::Release);
let prev = self.get_ref().head.swap(node.as_ptr(), Ordering::AcqRel);
(*prev).next.store(node.as_ptr(), Ordering::Release);
}
pub unsafe fn pop(self: Pin<&Self>) -> Pop {
let mut tail = self.tail.get();
let mut next = (*tail).next.load(Ordering::Acquire);
if core::ptr::eq(tail, &self.get_ref().stub) {
if next.is_null() {
return Pop::Empty;
}
self.tail.set(next);
tail = next;
next = (*next).next.load(core::sync::atomic::Ordering::Acquire);
}
if !next.is_null() {
self.tail.set(next);
return Pop::Value(NonNull::new_unchecked(tail as *mut NodeHeader));
}
let head = self.head.load(Ordering::Acquire);
if !core::ptr::eq(head, tail) {
return Pop::Inconsistant;
}
self.push(NonNull::from(&self.get_ref().stub));
next = (*tail).next.load(Ordering::Acquire);
if !next.is_null() {
self.tail.set(next);
return Pop::Value(NonNull::new_unchecked(tail as *mut NodeHeader));
}
Pop::Empty
}
}