use super::{internal::*, pointer::*};
use alloc::boxed::Box;
use core::{ptr, sync::atomic::{AtomicPtr, Ordering}};
use crossbeam_epoch::pin;
use crossbeam_utils::{Backoff, CachePadded};
#[derive(Debug)]
pub struct AtomicXarc<T: Send> {
pub(crate) ptr: CachePadded<AtomicPtr<XarcData<T>>>,
}
impl<T: Send> AtomicXarc<T> {
#[must_use]
pub fn new(value: T) -> Self {
AtomicXarc {
ptr: CachePadded::new(AtomicPtr::new(Box::into_raw(Box::new(XarcData::new(value))))),
}
}
#[must_use]
pub fn null() -> Self {
AtomicXarc {
ptr: CachePadded::new(AtomicPtr::new(ptr::null_mut())),
}
}
#[must_use]
pub(crate) fn init(ptr: *mut XarcData<T>) -> Self {
AtomicXarc {
ptr: CachePadded::new(AtomicPtr::new(ptr)),
}
}
#[must_use]
pub fn compare_and_swap(&self, current: &Xarc<T>, new: &Xarc<T>, success: Ordering, failure: Ordering) -> Xarc<T> {
match self.compare_exchange(current, new, success, failure) {
Ok(ptr) => ptr,
Err(ptr) => ptr,
}
}
pub fn compare_exchange(&self, current: &Xarc<T>, new: &Xarc<T>, success: Ordering, failure: Ordering) -> Result<Xarc<T>, Xarc<T>> {
let guard = pin();
unguarded_increment(new.ptr);
match self.ptr.compare_exchange(current.ptr, new.ptr, success, failure) {
Ok(ptr) => {
Ok(Xarc::init(ptr))
},
Err(ptr) => {
decrement(new.ptr, &guard);
Err(self.increment_or_reload(ptr, failure))
},
}
}
pub fn compare_exchange_weak(&self, current: &Xarc<T>, new: &Xarc<T>, success: Ordering, failure: Ordering) -> Result<Xarc<T>, Xarc<T>> {
let guard = pin();
unguarded_increment(new.ptr);
match self.ptr.compare_exchange_weak(current.ptr, new.ptr, success, failure) {
Ok(ptr) => {
Ok(Xarc::init(ptr))
},
Err(ptr) => {
decrement(new.ptr, &guard);
Err(self.increment_or_reload(ptr, failure))
},
}
}
#[must_use]
pub fn load(&self, order: Ordering) -> Xarc<T> {
let guard = pin();
let backoff = Backoff::new();
loop {
if let Ok(pointer) = Xarc::try_from(self.ptr.load(order), &guard) {
return pointer;
}
else {
backoff.spin();
}
}
}
#[allow(clippy::result_unit_err)]
pub fn try_load(&self, order: Ordering) -> Result<Xarc<T>, ()> {
let guard = pin();
Xarc::try_from(self.ptr.load(order), &guard)
}
#[must_use]
pub fn swap(&self, new: &Xarc<T>, order: Ordering) -> Xarc<T> {
unguarded_increment(new.ptr);
Xarc::init(self.ptr.swap(new.ptr, order))
}
#[must_use]
fn increment_or_reload(&self, ptr: *mut XarcData<T>, order: Ordering) -> Xarc<T> {
let guard = pin();
if try_increment(ptr, &guard).is_ok() {
Xarc::init(ptr)
}
else {
self.load(order)
}
}
}
impl<T: Send> Drop for AtomicXarc<T> {
fn drop(&mut self) {
let ptr = self.ptr.load(Ordering::Relaxed);
decrement(ptr, &pin());
}
}
impl<T: Send> From<&Xarc<T>> for AtomicXarc<T> {
#[must_use]
fn from(pointer: &Xarc<T>) -> Self {
unguarded_increment(pointer.ptr);
AtomicXarc::init(pointer.ptr)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xarc_simple_st_test() {
let shared = AtomicXarc::new(42);
let local = shared.try_load(Ordering::Acquire).unwrap();
drop(shared);
assert_eq!(*local.maybe_deref().unwrap(), 42);
}
}