use crate::sync::{Node, RawExt, WeakNode};
use std::{
fmt::Debug,
marker::PhantomData,
mem::ManuallyDrop,
ptr,
sync::{
Arc, Weak,
atomic::{AtomicPtr, Ordering},
},
};
pub type AtomicArc<T> = AtomicP<T, Arc<T>>;
pub type AtomicWeak<T> = AtomicP<T, Weak<T>>;
pub type AtomicNode<T> = AtomicP<T, Node<T>>;
pub type AtomicWeakNode<T> = AtomicP<T, WeakNode<T>>;
pub type NonNullAtomicNode<T> = NonNullAtomicP<T, Node<T>>;
pub type NonNullAtomicWeakNode<T> = NonNullAtomicP<T, WeakNode<T>>;
#[repr(transparent)]
#[derive(Clone)]
pub struct NonNullAtomicP<T, P: RawExt<T>> {
inner: AtomicP<T, P>,
}
impl<T, P: RawExt<T>> Debug for NonNullAtomicP<T, P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NonNullAtomicP")
.field("inner", &self.inner)
.finish()
}
}
impl<T, P: Clone + RawExt<T>> TryFrom<AtomicP<T, P>> for NonNullAtomicP<T, P> {
type Error = AtomicP<T, P>;
fn try_from(value: AtomicP<T, P>) -> Result<Self, Self::Error> {
if value.load(Ordering::Relaxed).is_some() {
Ok(Self { inner: value })
} else {
Err(value)
}
}
}
impl<T, P: RawExt<T>> From<NonNullAtomicP<T, P>> for AtomicP<T, P> {
fn from(value: NonNullAtomicP<T, P>) -> Self {
value.inner
}
}
impl<T, P: RawExt<T>> NonNullAtomicP<T, P> {
pub fn new(p: P) -> Self {
Self {
inner: AtomicP::new(p),
}
}
pub unsafe fn null() -> Self {
Self {
inner: AtomicP::null(),
}
}
pub fn as_atomic_p(&self) -> &AtomicP<T, P> {
&self.inner
}
pub unsafe fn take(&self, order: Ordering) -> P {
unsafe { self.inner.take(order).unwrap_unchecked() }
}
pub fn store(&self, new: P, order: Ordering) {
self.inner.store(new, order)
}
pub fn as_ptr(&self, order: Ordering) -> *mut T {
self.inner.as_ptr(order)
}
pub fn load(&self, order: Ordering) -> P
where
P: Clone,
{
unsafe { self.inner.load_unchecked(order) }
}
pub fn load_noclone(&self, order: Ordering) -> ManuallyDrop<P> {
unsafe { self.inner.load_manuallydrop_unchecked(order) }
}
pub fn swap(&self, new: P, order: Ordering) -> P {
unsafe { self.inner.swap_unchecked(new, order) }
}
pub fn compare_exchange(
&self,
current: &P,
new: P,
success: Ordering,
failure: Ordering,
) -> Result<P, CASErr<P>>
where
P: Clone,
{
unsafe {
self.inner
.compare_exchange_unchecked(current, new, success, failure)
}
}
pub fn into_p(self) -> P {
unsafe { ptr_to_p(self.inner.ptr.swap(ptr::null_mut(), Ordering::AcqRel)) }
}
}
pub struct AtomicP<T, P: RawExt<T>> {
ptr: AtomicPtr<T>,
_marker: PhantomData<P>,
}
impl<T, P: RawExt<T>> Debug for AtomicP<T, P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AtomicP").field("ptr", &self.ptr).finish()
}
}
impl<T, P: RawExt<T>> AtomicP<T, P> {
pub fn new(p: P) -> Self {
let raw = P::into_raw(p);
Self {
ptr: AtomicPtr::new(raw as *mut T),
_marker: PhantomData,
}
}
pub fn null() -> Self {
Self {
ptr: AtomicPtr::new(ptr::null_mut()),
_marker: PhantomData,
}
}
pub fn load(&self, order: Ordering) -> Option<P>
where
P: Clone,
{
let p = self.ptr.load(order);
if p.is_null() {
return None;
}
unsafe {
let out = P::from_raw(p);
let _extra = ManuallyDrop::new(out.clone());
Some(out)
}
}
pub unsafe fn load_unchecked(&self, order: Ordering) -> P
where
P: Clone,
{
unsafe {
let out = P::from_raw(self.ptr.load(order));
let _extra = ManuallyDrop::new(out.clone());
out
}
}
pub unsafe fn load_manuallydrop_unchecked(&self, order: Ordering) -> ManuallyDrop<P> {
unsafe { ManuallyDrop::new(P::from_raw(self.ptr.load(order))) }
}
pub fn as_ptr(&self, order: Ordering) -> *mut T {
self.ptr.load(order)
}
pub fn swap(&self, new: Option<P>, order: Ordering) -> Option<P> {
unsafe { ptr_to_opt_p(self.ptr.swap(opt_p_to_ptr(new), order)) }
}
pub unsafe fn swap_unchecked(&self, new: P, order: Ordering) -> P {
unsafe { ptr_to_p(self.ptr.swap(p_to_ptr(new), order)) }
}
pub fn store(&self, new: P, order: Ordering) {
drop(self.swap(Some(new), order))
}
pub fn take(&self, order: Ordering) -> Option<P> {
unsafe { ptr_to_opt_p(self.ptr.swap(ptr::null_mut(), order)) }
}
pub unsafe fn compare_exchange_unchecked(
&self,
current: &P,
new: P,
success: Ordering,
failure: Ordering,
) -> Result<P, CASErr<P>>
where
P: Clone,
{
let new_raw = p_to_ptr(new);
match self
.ptr
.compare_exchange(P::as_ptr(current) as *mut T, new_raw, success, failure)
{
Ok(old_raw) => Ok(unsafe { ptr_to_p(old_raw) }),
Err(actual_raw) => {
let actual = unsafe { P::from_raw(actual_raw) };
let _extra = ManuallyDrop::new(actual.clone());
Err(CASErr {
actual,
new: unsafe { ptr_to_p(new_raw) },
})
}
}
}
pub fn compare_exchange(
&self,
current: Option<&P>,
new: Option<P>,
success: Ordering,
failure: Ordering,
) -> Result<Option<P>, CASErr<Option<P>>>
where
P: Clone,
{
let new_raw = opt_p_to_ptr(new);
match self.ptr.compare_exchange(
current
.map(P::as_ptr)
.map(|p| p as *mut T)
.unwrap_or(ptr::null_mut()),
new_raw,
success,
failure,
) {
Ok(old_raw) => Ok(unsafe { ptr_to_opt_p(old_raw) }),
Err(actual_raw) => {
let actual = if actual_raw.is_null() {
None
} else {
let out = unsafe { P::from_raw(actual_raw) };
let _extra = ManuallyDrop::new(out.clone());
Some(out)
};
Err(CASErr {
actual,
new: unsafe { ptr_to_opt_p(new_raw) },
})
}
}
}
pub fn try_into_p(self) -> Option<P> {
self.take(Ordering::Relaxed)
}
}
impl<T, P: Clone + RawExt<T>> Clone for AtomicP<T, P> {
fn clone(&self) -> Self {
let out = Self::null();
let _ = out.swap(self.load(Ordering::Relaxed), Ordering::Relaxed);
out
}
}
impl<T, P: RawExt<T>> Drop for AtomicP<T, P> {
fn drop(&mut self) {
drop(self.take(Ordering::Relaxed));
}
}
pub struct CASErr<T> {
pub actual: T,
pub new: T,
}
fn p_to_ptr<T, P: RawExt<T>>(p: P) -> *mut T {
P::into_raw(p) as *mut T
}
fn opt_p_to_ptr<T, P: RawExt<T>>(opt_p: Option<P>) -> *mut T {
match opt_p {
Some(p) => p_to_ptr(p),
None => ptr::null_mut(),
}
}
unsafe fn ptr_to_opt_p<T, P: RawExt<T>>(ptr: *mut T) -> Option<P> {
if ptr.is_null() {
None
} else {
Some(unsafe { ptr_to_p(ptr) })
}
}
unsafe fn ptr_to_p<T, P: RawExt<T>>(ptr: *mut T) -> P {
unsafe { P::from_raw(ptr) }
}