use core::{
iter::FusedIterator,
mem,
ptr::{self, NonNull},
};
use alloc::boxed::Box;
use crate::loomish::sync::atomic::{AtomicPtr, Ordering::*};
pub struct Stack<T> {
head: AtomicPtr<Node<T>>,
}
impl<T> Stack<T> {
#[must_use]
pub fn new() -> Self {
Self {
head: AtomicPtr::new(ptr::null_mut()),
}
}
pub unsafe fn push(&self, node: NonNull<Node<T>>) {
let node = node.as_ptr();
self.head
.fetch_update(Release, Relaxed, |head| {
unsafe { (*node).next.store(head, Relaxed) };
Some(node)
})
.unwrap();
}
#[must_use]
pub unsafe fn pop(&self) -> Option<NonNull<Node<T>>> {
self.head
.fetch_update(Relaxed, Acquire, |head| {
(!head.is_null()).then(|| unsafe { (*head).next.load(Relaxed) })
})
.ok()
.map(|node| unsafe { NonNull::new_unchecked(node) })
}
pub unsafe fn swap(&self, mut other: Self) -> Self {
let other = mem::take(&mut other.head).into_inner();
let head = self.head.swap(other, AcqRel);
Self {
head: AtomicPtr::new(head),
}
}
}
impl<T> Default for Stack<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Drop for Stack<T> {
fn drop(&mut self) {
let mut head = mem::take(&mut self.head).into_inner();
while !head.is_null() {
head = unsafe { *Box::from_raw(head) }.next.into_inner();
}
}
}
unsafe impl<T: Send> Send for Stack<T> {}
unsafe impl<T: Send> Sync for Stack<T> {}
impl<T> IntoIterator for Stack<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(mut self) -> Self::IntoIter {
let head = mem::take(&mut self.head).into_inner();
IntoIter { head }
}
}
#[repr(C)] pub struct Node<T> {
pub next: AtomicPtr<Self>,
pub item: T,
}
impl<T> Node<T> {
#[must_use]
pub fn new(item: T) -> NonNull<Self> {
let next = AtomicPtr::new(ptr::null_mut());
let this = Box::new(Self { next, item });
unsafe { NonNull::new_unchecked(Box::into_raw(this)) }
}
}
pub struct IntoIter<T> {
head: *mut Node<T>,
}
impl<T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.head.is_null() {
return None;
}
let Node { next, item } = unsafe { *Box::from_raw(self.head) };
self.head = next.into_inner();
Some(item)
}
}
impl<T> FusedIterator for IntoIter<T> {}
#[cfg(test)]
mod tests {
use alloc::boxed::Box;
use super::{Node, Stack};
#[test]
fn push_pop() {
let stack = Stack::new();
unsafe {
stack.push(Node::new(47));
stack.push(Node::new(752));
stack.push(Node::new(1367));
assert_eq!(Box::from_raw(stack.pop().unwrap().as_ptr()).item, 1367);
assert_eq!(Box::from_raw(stack.pop().unwrap().as_ptr()).item, 752);
assert_eq!(Box::from_raw(stack.pop().unwrap().as_ptr()).item, 47);
assert!(stack.pop().is_none());
}
}
}