use alloc::boxed::Box;
use core::{
ptr,
sync::atomic::{AtomicPtr, Ordering},
};
use ruspiro_arch_aarch64::instructions::*;
#[derive(Debug)]
#[repr(align(16))]
struct Node<T: Sized> {
next: AtomicPtr<Node<T>>,
value: Option<T>,
}
impl<T: Sized> Node<T> {
fn new(value: T) -> Box<Node<T>> {
Box::new(Node {
next: AtomicPtr::new(core::ptr::null_mut()),
value: Some(value),
})
}
}
#[derive(Debug)]
pub enum Pop<T: Sized + 'static> {
Empty,
Data(T),
Intermediate,
}
pub struct Queue<T: Sized + 'static> {
head: AtomicPtr<Node<T>>,
tail: AtomicPtr<Node<T>>,
}
impl<T: Sized + 'static> Queue<T> {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
head: AtomicPtr::new(ptr::null_mut()),
tail: AtomicPtr::new(ptr::null_mut()),
}
}
pub fn push(&self, value: T) {
let node = Box::into_raw(Node::new(value));
dmb();
let old_node = self.head.swap(node, Ordering::AcqRel);
dsb();
if !old_node.is_null() {
unsafe {
(*old_node).next.store(node, Ordering::SeqCst);
}
}
dmb();
let _ = self
.tail
.compare_exchange(ptr::null_mut(), node, Ordering::AcqRel, Ordering::Relaxed);
dsb();
}
pub fn pop(&self) -> Pop<T> {
dmb();
let node = self.tail.swap(ptr::null_mut(), Ordering::AcqRel);
dsb(); if node.is_null() {
return Pop::Intermediate;
}
let _ = self
.head
.compare_exchange(node, ptr::null_mut(), Ordering::AcqRel, Ordering::Relaxed);
dsb();
let node = unsafe { Box::from_raw(node) };
let next_node = node.next.load(Ordering::Acquire);
if !next_node.is_null() {
let _ = self.tail.compare_exchange(
ptr::null_mut(),
next_node,
Ordering::AcqRel,
Ordering::Relaxed,
);
dsb(); }
let value = node.value.unwrap();
Pop::Data(value)
}
}
impl<T: Sized + 'static> Drop for Queue<T> {
fn drop(&mut self) {
while let Pop::Data(_) = self.pop() {}
}
}