use core::fmt;
use core::ptr::NonNull;
use core::sync::atomic::Ordering;
use crate::{Tag, ptr::{Ptr, TaggedPtr}};
#[cfg(all(target_pointer_width = "64", not(atomic_fallback)))]
mod ptr64;
#[cfg(all(target_pointer_width = "64", not(atomic_fallback)))]
use ptr64::AtomicTaggedPtrImpl;
#[cfg(all(target_pointer_width = "64", not(atomic_fallback)))]
pub use ptr64::TAG_MASK;
#[cfg(all(target_pointer_width = "32", not(atomic_fallback)))]
mod ptr32;
#[cfg(all(target_pointer_width = "32", not(atomic_fallback)))]
use ptr32::AtomicTaggedPtrImpl;
#[cfg(all(target_pointer_width = "32", not(atomic_fallback)))]
pub use ptr32::TAG_MASK;
#[cfg(atomic_fallback)]
mod fallback;
#[cfg(atomic_fallback)]
pub use fallback::TAG_MASK;
#[cfg(atomic_fallback)]
use fallback::AtomicTaggedPtrImpl;
pub type TaggedPtrResult<T> = Result<TaggedPtr<T>, TaggedPtr<T>>;
pub(crate) type RawTaggedPtrResult<T> =
Result<(Option<NonNull<T>>, Tag), (Option<NonNull<T>>, Tag)>;
pub struct AtomicTaggedPtr<T> {
inner: AtomicTaggedPtrImpl<T>,
}
unsafe impl<T> Send for AtomicTaggedPtr<T> {}
unsafe impl<T> Sync for AtomicTaggedPtr<T> {}
impl<T> AtomicTaggedPtr<T> {
#[inline]
pub fn new(val: impl Into<TaggedPtr<T>>) -> Self {
let val = val.into();
Self {
inner: AtomicTaggedPtrImpl::new(val.ptr.option(), val.tag),
}
}
#[inline]
pub fn load(&self, order: Ordering) -> TaggedPtr<T> {
let (raw_ptr, tag) = self.inner.load(order);
TaggedPtr {
ptr: Ptr::new(raw_ptr),
tag,
}
}
#[inline]
pub fn store(&self, val: impl Into<TaggedPtr<T>>, order: Ordering) {
let val = val.into();
self.inner.store(val.ptr.option(), val.tag, order);
}
#[inline]
pub fn compare_exchange(
&self,
current: impl Into<TaggedPtr<T>>,
new: impl Into<TaggedPtr<T>>,
success: Ordering,
failure: Ordering,
) -> TaggedPtrResult<T> {
let current = current.into();
let new = new.into();
match self.inner.compare_exchange(
(current.ptr.option(), current.tag),
(new.ptr.option(), new.tag),
success,
failure,
) {
Ok((raw_ptr, tag)) => Ok(TaggedPtr {
ptr: Ptr::new(raw_ptr),
tag,
}),
Err((raw_ptr, tag)) => Err(TaggedPtr {
ptr: Ptr::new(raw_ptr),
tag,
}),
}
}
#[inline]
pub fn compare_exchange_weak(
&self,
current: impl Into<TaggedPtr<T>>,
new: impl Into<TaggedPtr<T>>,
success: Ordering,
failure: Ordering,
) -> TaggedPtrResult<T> {
let current = current.into();
let new = new.into();
match self.inner.compare_exchange_weak(
(current.ptr.option(), current.tag),
(new.ptr.option(), new.tag),
success,
failure,
) {
Ok((raw_ptr, tag)) => Ok(TaggedPtr {
ptr: Ptr::new(raw_ptr),
tag,
}),
Err((raw_ptr, tag)) => Err(TaggedPtr {
ptr: Ptr::new(raw_ptr),
tag,
}),
}
}
#[inline]
pub fn swap(&self, val: impl Into<TaggedPtr<T>>, order: Ordering) -> TaggedPtr<T> {
let val = val.into();
let (raw_ptr, tag) = self.inner.swap(val.ptr.option(), val.tag, order);
TaggedPtr {
ptr: Ptr::new(raw_ptr),
tag,
}
}
#[inline]
pub fn into_inner(self) -> TaggedPtr<T> {
let (raw_ptr, tag) = self.inner.into_inner();
TaggedPtr {
ptr: Ptr::new(raw_ptr),
tag,
}
}
#[inline]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<TaggedPtr<T>, TaggedPtr<T>>
where
F: FnMut(TaggedPtr<T>) -> Option<TaggedPtr<T>>,
{
let mut prev = self.load(fetch_order);
while let Some(next) = f(prev) {
match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
Ok(x) => return Ok(x),
Err(next_prev) => prev = next_prev,
}
}
Err(prev)
}
#[inline]
pub fn fetch_add_tag(&self, val: usize, order: Ordering) -> TaggedPtr<T> {
let (success, failure) = split_ordering(order);
self.fetch_update(success, failure, |prev| {
Some(prev.with_tag(prev.tag.wrapping_add(val)))
}).unwrap()
}
#[inline]
pub fn fetch_sub_tag(&self, val: usize, order: Ordering) -> TaggedPtr<T> {
let (success, failure) = split_ordering(order);
self.fetch_update(success, failure, |prev| {
Some(prev.with_tag(prev.tag.wrapping_sub(val)))
}).unwrap()
}
#[inline]
pub fn fetch_and_tag(&self, val: usize, order: Ordering) -> TaggedPtr<T> {
let (success, failure) = split_ordering(order);
self.fetch_update(success, failure, |prev| {
Some(prev.with_tag(prev.tag & val))
}).unwrap()
}
#[inline]
pub fn fetch_or_tag(&self, val: usize, order: Ordering) -> TaggedPtr<T> {
let (success, failure) = split_ordering(order);
self.fetch_update(success, failure, |prev| {
Some(prev.with_tag(prev.tag | val))
}).unwrap()
}
#[inline]
pub fn fetch_xor_tag(&self, val: usize, order: Ordering) -> TaggedPtr<T> {
let (success, failure) = split_ordering(order);
self.fetch_update(success, failure, |prev| {
Some(prev.with_tag(prev.tag ^ val))
}).unwrap()
}
#[inline]
pub fn fetch_set_ptr(&self, ptr: impl Into<Ptr<T>>, order: Ordering) -> TaggedPtr<T> {
let (success, failure) = split_ordering(order);
let ptr = ptr.into();
self.fetch_update(success, failure, |prev| {
Some(prev.with_ptr(ptr))
}).unwrap()
}
#[inline]
pub fn fetch_set_tag(&self, tag: Tag, order: Ordering) -> TaggedPtr<T> {
let (success, failure) = split_ordering(order);
self.fetch_update(success, failure, |prev| {
Some(prev.with_tag(tag))
}).unwrap()
}
}
#[inline]
const fn split_ordering(order: Ordering) -> (Ordering, Ordering) {
match order {
Ordering::SeqCst => (Ordering::SeqCst, Ordering::SeqCst),
Ordering::AcqRel => (Ordering::AcqRel, Ordering::Acquire),
Ordering::Acquire => (Ordering::Acquire, Ordering::Acquire),
Ordering::Release => (Ordering::Release, Ordering::Relaxed),
Ordering::Relaxed => (Ordering::Relaxed, Ordering::Relaxed),
_ => (order, Ordering::Relaxed),
}
}
impl<T> Default for AtomicTaggedPtr<T> {
#[inline]
fn default() -> Self {
Self::new(TaggedPtr::default())
}
}
impl<T> fmt::Debug for AtomicTaggedPtr<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let val = self.load(Ordering::Relaxed);
f.debug_struct("AtomicTaggedPtr")
.field("pointer", &val.ptr)
.field("tag", &val.tag)
.finish()
}
}
impl<T> From<TaggedPtr<T>> for AtomicTaggedPtr<T> {
#[inline]
fn from(val: TaggedPtr<T>) -> Self {
Self::new(val)
}
}
impl<T> From<(Ptr<T>, Tag)> for AtomicTaggedPtr<T> {
#[inline]
fn from(val: (Ptr<T>, Tag)) -> Self {
Self::new(val)
}
}
impl<T> From<Ptr<T>> for AtomicTaggedPtr<T> {
#[inline]
fn from(ptr: Ptr<T>) -> Self {
Self::new(TaggedPtr::new(ptr, Tag::default()))
}
}
impl<T> From<Option<NonNull<T>>> for AtomicTaggedPtr<T> {
#[inline]
fn from(ptr: Option<NonNull<T>>) -> Self {
Self::new(TaggedPtr::new(ptr, Tag::default()))
}
}
impl<T> From<NonNull<T>> for AtomicTaggedPtr<T> {
#[inline]
fn from(ptr: NonNull<T>) -> Self {
Self::new(TaggedPtr::new(ptr, Tag::default()))
}
}
impl<T> From<*const T> for AtomicTaggedPtr<T> {
#[inline]
fn from(ptr: *const T) -> Self {
Self::new(TaggedPtr::new(ptr, Tag::default()))
}
}
impl<T> From<*mut T> for AtomicTaggedPtr<T> {
#[inline]
fn from(ptr: *mut T) -> Self {
Self::new(TaggedPtr::new(ptr, Tag::default()))
}
}
#[cfg(all(test, feature = "std"))]
mod tests;