use alloc::sync::Arc;
use core::mem::ManuallyDrop;
use core::ptr;
use core::sync::atomic::Ordering;
use crate::link::LinkWrapper;
use crate::ArcPointer;
#[inline]
fn ptr_to_arc<T>(ptr: *const T) -> Option<Arc<T>> {
unsafe { ArcPointer::from_raw(ptr) }
}
#[derive(Debug)]
pub struct RcuCell<T> {
link: LinkWrapper<T>,
}
unsafe impl<T: Send> Send for RcuCell<T> {}
unsafe impl<T: Send + Sync> Sync for RcuCell<T> {}
impl<T> Drop for RcuCell<T> {
fn drop(&mut self) {
let ptr = self.link.get_ref();
let _ = ptr_to_arc(ptr);
}
}
impl<T> Default for RcuCell<T> {
fn default() -> Self {
RcuCell::none()
}
}
impl<T> From<Arc<T>> for RcuCell<T> {
fn from(data: Arc<T>) -> Self {
let arc_ptr = Arc::into_raw(data);
RcuCell {
link: LinkWrapper::new(arc_ptr),
}
}
}
impl<T> From<Option<Arc<T>>> for RcuCell<T> {
fn from(data: Option<Arc<T>>) -> Self {
let ptr = data.into_raw();
RcuCell {
link: LinkWrapper::new(ptr),
}
}
}
impl<T> RcuCell<T> {
#[inline]
pub const fn none() -> Self {
RcuCell {
link: LinkWrapper::new(ptr::null()),
}
}
#[inline]
pub fn some(data: T) -> Self {
let ptr = Arc::into_raw(Arc::new(data));
RcuCell {
link: LinkWrapper::new(ptr),
}
}
#[inline]
pub fn new(data: impl Into<Option<T>>) -> Self {
let data = data.into();
match data {
Some(data) => Self::some(data),
None => Self::none(),
}
}
#[inline]
pub fn into_arc(self) -> Option<Arc<T>> {
let ptr = self.link.get_ref();
let ret = ptr_to_arc(ptr);
let _ = ManuallyDrop::new(self);
ret
}
#[inline]
pub fn is_none(&self) -> bool {
self.link.is_none()
}
#[inline]
pub fn set(&self, data: Option<Arc<T>>) -> Option<Arc<T>> {
let new_ptr = data.into_raw();
ptr_to_arc(self.link.update(new_ptr))
}
#[inline]
pub fn take(&self) -> Option<Arc<T>> {
self.set(None)
}
#[inline]
pub fn write(&self, data: impl Into<Arc<T>>) -> Option<Arc<T>> {
let data = data.into();
self.set(Some(data))
}
pub fn update<R, F>(&self, f: F) -> Option<Arc<T>>
where
F: FnOnce(Option<Arc<T>>) -> Option<R>,
R: Into<Arc<T>>,
{
let ptr = self.link.lock_read();
let old_value = ptr_to_arc(ptr);
let new_ptr = match f(old_value.clone()) {
Some(data) => Arc::into_raw(data.into()),
None => ptr::null_mut(),
};
self.link.unlock_update(new_ptr);
old_value
}
pub unsafe fn compare_exchange<'a>(
&self,
current: *const T,
new: Option<&'a Arc<T>>,
success: Ordering,
failure: Ordering,
) -> Result<*const T, *const T>
where
T: 'a,
{
let new_ptr = match new {
Some(data) => Arc::as_ptr(data),
None => ptr::null(),
};
self.link
.compare_exchange(current, new_ptr, success, failure)
.inspect(|ptr| {
let _ = ptr_to_arc(ptr);
if let Some(v) = new {
let _ = Arc::into_raw(Arc::clone(v));
}
})
}
#[inline]
pub fn read(&self) -> Option<Arc<T>> {
let ptr = self.link.inc_ref();
let v = ManuallyDrop::new(ptr_to_arc(ptr));
let cloned = v.as_ref().cloned();
self.link.dec_ref();
core::sync::atomic::fence(Ordering::Acquire);
cloned
}
#[inline]
pub fn arc_eq(&self, data: &Arc<T>) -> bool {
core::ptr::eq(self.link.get_ref(), Arc::as_ptr(data))
}
#[inline]
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
core::ptr::eq(this.link.get_ref(), other.link.get_ref())
}
}