use std::ops::Deref;
use std::sync::atomic::{AtomicPtr, Ordering};
use std::sync::{Arc, Mutex};
use once_cell::sync::OnceCell;
pub struct Rcu<T>(AtomicPtr<T>);
unsafe impl<T: Sync> Sync for Rcu<T> {}
unsafe impl<T: Send> Send for Rcu<T> {}
impl<T> Clone for Rcu<T> {
fn clone(&self) -> Self {
let p = self.0.swap(std::ptr::null_mut(), Ordering::Acquire);
let arc: Arc<T> = unsafe { Arc::from_raw(p) };
let other = arc.clone();
let _copy_i_am_keeping = Arc::into_raw(arc);
Rcu(AtomicPtr::new(Arc::into_raw(other) as *mut T))
}
}
impl<T> Drop for Rcu<T> {
fn drop(&mut self) {
let p = self.0.swap(std::ptr::null_mut(), Ordering::Acquire);
let _to_free = unsafe { Arc::from_raw(p) };
}
}
impl<T> From<Arc<T>> for Rcu<T> {
fn from(b: Arc<T>) -> Self {
Rcu(std::sync::atomic::AtomicPtr::new(Arc::into_raw(b) as *mut T))
}
}
impl<T: Clone + Send + Sync + 'static> Rcu<T> {
pub fn new(value: T) -> Self {
Self::from(Arc::new(value))
}
pub fn read<'a, 'b: 'a>(&'b self, _grace: &'a Grace) -> RcuGuard<'a, T> {
let p = self.0.load(Ordering::Acquire);
RcuGuard {
ptr: unsafe { &*p },
}
}
pub fn update(&self, f: impl FnOnce(&mut T)) {
let mut new = Arc::new(self.read(&Grace::new()).clone());
f(Arc::get_mut(&mut new).unwrap());
let mut lock = GRACE.get().unwrap().0.lock().unwrap();
let mut vec_lock = lock.lock().unwrap();
let old = self.0.swap(Arc::into_raw(new) as *mut T, Ordering::Release);
vec_lock.push(unsafe { Arc::from_raw(old) });
let next_grace = Arc::new(Mutex::new(Vec::new()));
vec_lock.push(Arc::new(next_grace.clone()));
drop(vec_lock);
*lock = next_grace;
}
}
static GRACE: OnceCell<SourceOfGrace> = OnceCell::new();
#[derive(Clone)]
pub struct Grace {
_to_free: Arc<Mutex<Vec<Arc<dyn Send + Sync>>>>,
}
impl Grace {
pub fn new() -> Grace {
Grace {
_to_free: GRACE
.get_or_init(|| SourceOfGrace(Mutex::new(Arc::new(Mutex::new(Vec::new())))))
.0
.lock()
.unwrap()
.clone(),
}
}
}
struct SourceOfGrace(Mutex<Arc<Mutex<Vec<Arc<dyn Send + Sync>>>>>);
pub struct RcuGuard<'a, T> {
ptr: &'a T,
}
impl<'a, T> Deref for RcuGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.ptr
}
}