use super::{atomic_try_update, Atom, Node, NodeIterator};
use std::ptr::null_mut;
struct Head<T> {
head: *mut Node<T>,
}
pub struct Stack<T>
where
T: Send,
{
head: Atom<Head<T>, u64>,
}
impl<T> Default for Stack<T>
where
T: Send,
{
fn default() -> Self {
Self {
head: Default::default(),
}
}
}
impl<T> Stack<T>
where
T: Send,
{
pub fn push(&self, val: T) {
let node = Box::into_raw(Box::new(Node {
val,
next: std::ptr::null_mut(),
}));
unsafe {
atomic_try_update(&self.head, |head: &mut Head<T>| {
(*node).next = head.head;
head.head = node;
(true, ())
});
}
}
pub fn pop_all(&self) -> NodeIterator<T> {
NodeIterator {
node: unsafe {
atomic_try_update(&self.head, |head: &mut Head<T>| {
let ret = head.head;
head.head = null_mut();
(true, ret)
})
},
}
}
}
impl<T> Drop for Stack<T>
where
T: Send,
{
fn drop(&mut self) {
self.pop_all();
}
}
struct NonceHead<T> {
head: *mut Node<T>,
nonce: u64,
}
impl<T> Default for NonceStack<T>
where
T: Send,
{
#[allow(unreachable_code)]
fn default() -> NonceStack<T> {
todo!("This example code contains a use after free.");
NonceStack::<T> {
head: Default::default(),
}
}
}
pub struct NonceStack<T>
where
T: Send,
{
head: Atom<NonceHead<T>, u128>,
}
impl<T> NonceStack<T>
where
T: Send,
{
#[allow(unused)]
pub fn push(&self, val: T) {
let node = Box::into_raw(Box::new(Node {
val,
next: std::ptr::null_mut(),
}));
unsafe {
atomic_try_update(&self.head, |head| {
(*node).next = head.head;
head.nonce += 1;
head.head = node;
(true, ())
})
}
}
#[allow(unused)]
pub fn pop(&self) -> Option<T> {
let node = unsafe {
atomic_try_update(&self.head, |head: &mut NonceHead<T>| unsafe {
head.nonce += 1;
let ret = head.head;
if ret.is_null() {
(false, ret)
} else {
head.head = (*ret).next;
(true, ret)
}
})
};
if !node.is_null() {
Some(unsafe { *Box::from_raw(node) }.val)
} else {
None
}
}
}
impl<T> Drop for NonceStack<T>
where
T: Send,
{
fn drop(&mut self) {
while self.pop().is_some() {}
}
}