#![cfg(target_has_atomic = "128")]
#![feature(allocator_api)]
#![feature(integer_atomics)]
#![no_std]
extern crate alloc;
use alloc::boxed::Box;
use core::{
hint,
marker::PhantomData,
ops::Deref,
ptr::NonNull,
sync::atomic::{AtomicU128, Ordering},
};
pub struct RcuGuard<'a, T> {
ptr: NonNull<T>,
cell: &'a RcuCell<T>,
}
impl<T> Deref for RcuGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
}
impl<T> Drop for RcuGuard<'_, T> {
fn drop(&mut self) {
loop {
let ptr_counter_latest = self.cell.ptr_counter_latest.load(Ordering::Acquire);
if (ptr_counter_latest >> 64) as usize == self.ptr.as_ptr() as usize {
if self
.cell
.ptr_counter_latest
.compare_exchange_weak(
ptr_counter_latest,
ptr_counter_latest - 1,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_ok()
{
return;
}
} else {
break;
}
hint::spin_loop();
}
loop {
let ptr_counter_old = self.cell.ptr_counter_to_clear.load(Ordering::Acquire);
if (ptr_counter_old >> 64) as usize == self.ptr.as_ptr() as usize
&& self
.cell
.ptr_counter_to_clear
.compare_exchange_weak(
ptr_counter_old,
ptr_counter_old - 1,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_ok()
{
return;
}
hint::spin_loop();
}
}
}
pub struct RcuCell<T> {
ptr_counter_latest: AtomicU128,
ptr_counter_to_clear: AtomicU128,
data: PhantomData<T>,
}
impl<T> RcuCell<T> {
pub fn new(value: T) -> Self {
Self {
ptr_counter_latest: AtomicU128::new((Box::into_raw(Box::new(value)) as u128) << 64),
ptr_counter_to_clear: AtomicU128::new(0),
data: PhantomData,
}
}
pub fn read(&self) -> RcuGuard<'_, T> {
let ptr = unsafe {
NonNull::new_unchecked(
(self.ptr_counter_latest.fetch_add(1, Ordering::AcqRel) >> 64) as usize as *mut T,
)
};
RcuGuard { cell: self, ptr }
}
pub fn write(&self, value: T) {
let new_ptr_counter = (Box::into_raw(Box::new(value)) as u128) << 64;
let old_ptr_counter = self
.ptr_counter_latest
.swap(new_ptr_counter, Ordering::AcqRel);
if old_ptr_counter & 0xffff_ffff_ffff_ffff == 0 {
unsafe {
let _ = Box::from_raw((old_ptr_counter >> 64) as usize as *mut T);
}
return;
}
while self
.ptr_counter_to_clear
.compare_exchange_weak(0, old_ptr_counter, Ordering::Release, Ordering::Acquire)
.is_err()
{
hint::spin_loop();
}
while self.ptr_counter_to_clear.load(Ordering::Acquire) & 0xffff_ffff_ffff_ffff != 0 {
hint::spin_loop();
}
self.ptr_counter_to_clear.store(0, Ordering::Release);
unsafe {
let _ = Box::from_raw((old_ptr_counter >> 64) as usize as *mut T);
}
}
}