use std::{
num::NonZeroU64,
ptr::null_mut,
sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
};
use seize::{reclaim, Collector, Linked};
use crate::once_ptr::OncePtr;
pub struct SnapshotList<T, U> {
collector: Collector,
head: AtomicPtr<Linked<SnapshotInner<T, U>>>,
tail: AtomicPtr<Linked<SnapshotInner<T, U>>>,
}
impl<T, U> Default for SnapshotList<T, U>
where
U: Default,
{
fn default() -> Self {
Self::new(U::default())
}
}
impl<T, U> SnapshotList<T, U> {
pub fn new(metadata: U) -> Self {
let collector = Collector::new()
.batch_size(16)
.epoch_frequency(NonZeroU64::new(10));
let node = collector.link_boxed(SnapshotInner::empty(metadata));
SnapshotList {
collector,
head: AtomicPtr::new(node),
tail: AtomicPtr::new(node),
}
}
pub fn push<F>(&self, data: T, metadata: U, transition: F)
where
F: FnOnce(),
{
let guard = self.collector.enter();
let new_head = self.collector.link_boxed(SnapshotInner::empty(metadata));
let prev_head_ptr = guard.protect(&self.head, Ordering::Acquire);
debug_assert!(!prev_head_ptr.is_null());
let prev_head = unsafe { &*prev_head_ptr };
prev_head.data.store(data);
prev_head.next.store(new_head, Ordering::Relaxed);
transition();
self.head.store(new_head, Ordering::Relaxed);
let mut tail = self.tail.load(Ordering::Relaxed);
let original_tail = tail;
loop {
if tail == prev_head_ptr {
break;
}
debug_assert!(!tail.is_null());
let tail_ref = unsafe { &*tail };
let counter = tail_ref.counter.load(Ordering::Relaxed);
if counter > 0 {
break;
}
unsafe {
self.collector
.retire(tail, reclaim::boxed::<SnapshotInner<T, U>>);
}
tail = tail_ref.next.load(Ordering::Relaxed);
}
if tail != original_tail {
self.tail.store(tail, Ordering::Relaxed);
}
}
pub fn current(&self) -> Snapshot<T, U> {
let guard = self.collector.enter();
let head_ptr = guard.protect(&self.head, Ordering::Acquire);
Snapshot::new(head_ptr)
}
pub fn get_metadata_mut(&mut self) -> &mut U {
let head_ptr = self.head.load(Ordering::Relaxed);
let head = unsafe { &mut *head_ptr };
&mut head.metadata
}
}
pub struct Snapshot<T, U> {
inner: *mut Linked<SnapshotInner<T, U>>,
}
struct SnapshotInner<T, U> {
counter: AtomicUsize,
next: AtomicPtr<Linked<SnapshotInner<T, U>>>,
data: OncePtr<T>,
metadata: U,
}
impl<T, U> SnapshotInner<T, U> {
#[inline(always)]
pub fn empty(metadata: U) -> Self {
SnapshotInner {
counter: AtomicUsize::new(0),
next: AtomicPtr::new(null_mut()),
data: OncePtr::null(),
metadata,
}
}
}
impl<T, U> Snapshot<T, U> {
#[inline(always)]
fn new(inner_ptr: *mut Linked<SnapshotInner<T, U>>) -> Self {
debug_assert!(!inner_ptr.is_null());
let inner = unsafe { &*inner_ptr };
inner.counter.fetch_add(1, Ordering::Relaxed);
Self { inner: inner_ptr }
}
pub fn get_metadata(&self) -> &U {
let inner = unsafe { &*self.inner };
&inner.metadata
}
#[inline]
pub fn find<'a, F, R>(&'a self, predicate: F) -> Option<R>
where
F: Fn(&'a T) -> Option<R>,
{
let mut current_ptr = self.inner;
loop {
if current_ptr.is_null() {
break;
}
debug_assert!(!current_ptr.is_null());
let current = unsafe { &*current_ptr };
if let Some(data) = current.data.load() {
if let Some(result) = predicate(data) {
return Some(result);
}
} else {
break;
}
current_ptr = current.next.load(Ordering::Relaxed);
}
None
}
}
impl<T, U> Drop for Snapshot<T, U> {
fn drop(&mut self) {
debug_assert!(!self.inner.is_null());
let inner = unsafe { &*self.inner };
inner.counter.fetch_sub(1, Ordering::Relaxed);
}
}
impl<T, U> Drop for SnapshotList<T, U> {
fn drop(&mut self) {
let mut tail = self.tail.load(Ordering::Relaxed);
loop {
if tail.is_null() {
break;
}
debug_assert!(!tail.is_null());
let tail_ref = unsafe { &*tail };
unsafe {
self.collector
.retire(tail, reclaim::boxed::<SnapshotInner<T, U>>);
}
tail = tail_ref.next.load(Ordering::Relaxed);
}
}
}
#[cfg(tests)]
mod tests {
}