use std::mem::MaybeUninit; use sync::*; // pub(crate) use std::sync::atomic::{AtomicPtr, Ordering, fence}; mod sync; struct Stack<T> { head: AtomicPtr<Node<T>>, } impl<T> Drop for Stack<T> { fn drop(&mut self) { // todo!() } } unsafe impl<T: Send> Send for Stack<T> {} unsafe impl<T> Sync for Stack<T> {} struct Node<T> { data: MaybeUninit<T>, next: *mut Node<T>, } impl<T> Stack<T> { fn new() -> Self { Self { head: AtomicPtr::new(std::ptr::null_mut()), } } fn push(&self, data: T) { let node = Box::into_raw(Box::new(Node { data: MaybeUninit::new(data), next: std::ptr::null_mut(), })); let mut head = self.head.load(Ordering::Relaxed); loop { unsafe { &mut *node }.next = head; match self .head .compare_exchange_weak(head, node, Ordering::Release, Ordering::Relaxed) { Ok(_) => break, Err(actual) => { head = actual; } } } } fn pop(&self) -> Option<T> { let mut head = self.head.load(Ordering::Relaxed); // this fence prevents the above load from loading a detached head... // because a detached head is deallocated and we might dereference it to reach `next`. fence(Ordering::SeqCst); // a SC fence here (to synchronize with the below fence) loop { if head.is_null() { return None; } let next = unsafe { &*head }.next; match self .head .compare_exchange_weak(head, next, Ordering::Relaxed, Ordering::Relaxed) { Ok(head) => { fence(Ordering::SeqCst); // a SC fence here let data = unsafe { (&mut *head).data.assume_init_read() }; drop(unsafe { Box::from_raw(head) }); return Some(data); } Err(actual) => { head = actual; } } } } } some things that could be wrong and are confusing:
- mixing memory orderings with
SeqCst, should I avoid this? Is the behavior unpredictable? - Does SeqCst Ordering act correctly on the load? (an Acquire fence is placed below the load, so I just placed the SeqCst fence below as well.)
- Can the job be done with just a single SeqCst fence?
- How to test this? I tried testing this very basic implementation using loom, but loom doesn't seem to be complaining even when I comment out the fences

I know that I could have used some very nice crates for memory reclamation here, but I was just trying things. Same thing goes for using fences and not using Ordering::SeqCst directly on the atomic operation ![]()
13 posts - 4 participants