use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicPtr, Ordering};
pub(crate) struct AtomicSnapshot<T> {
read: AtomicPtr<T>,
retired: UnsafeCell<Vec<*mut T>>,
}
unsafe impl<T: Send> Send for AtomicSnapshot<T> {}
unsafe impl<T: Sync> Sync for AtomicSnapshot<T> {}
impl<T> AtomicSnapshot<T> {
pub fn new(initial: T) -> Self {
Self {
read: AtomicPtr::new(Box::into_raw(Box::new(initial))),
retired: UnsafeCell::new(Vec::new()),
}
}
#[inline]
pub fn load(&self) -> &T {
unsafe { &*self.read.load(Ordering::Acquire) }
}
pub fn publish(&self, new_snapshot: T) {
let new_ptr = Box::into_raw(Box::new(new_snapshot));
let old = self.read.swap(new_ptr, Ordering::AcqRel);
unsafe {
(*self.retired.get()).push(old);
}
}
fn release(&self) {
unsafe {
let retired = std::mem::take(&mut *self.retired.get());
for p in retired {
drop(Box::from_raw(p));
}
let current = self.read.load(Ordering::Relaxed);
drop(Box::from_raw(current));
}
}
}
impl<T> Drop for AtomicSnapshot<T> {
fn drop(&mut self) {
self.release();
}
}