use crate::sync::hash_set::FixedHashSet;
use crate::{
epoch::{Color, Phase},
guards::{Guard, Handle},
internal::HazardPointer,
tls::pin,
};
use std::{
any::TypeId,
borrow::Borrow,
hash::{Hash, Hasher},
hint::cold_path,
marker::PhantomData,
mem::forget,
ops::{ControlFlow, Deref},
ptr::{NonNull, null_mut},
sync::atomic::{AtomicPtr, AtomicU32, AtomicUsize, Ordering},
};
pub(crate) const TYPE_ID_BITS: u32 = 8;
const MAX_TYPE_ID: usize = 1 << TYPE_ID_BITS;
const_assert!(63 - TYPE_ID_BITS >= 32);
type ShadePointeeFn = unsafe fn(mobj_ptr: *mut (), guard: &Guard);
#[cfg(feature = "tag")]
pub type WithTag<L> = (L, usize);
#[cfg(not(feature = "tag"))]
pub(crate) type WithTag<L> = (L, usize);
pub type CasResult<L> = Result<L, L>;
type RootedCasFallback<'g, T> =
ControlFlow<CasResult<WithTag<Option<Local<'g, Guard, T>>>>, ManPtr<T>>;
struct TypeReg {
type_id: TypeId,
id: AtomicU32, }
impl Hash for TypeReg {
fn hash<H: Hasher>(&self, state: &mut H) {
self.type_id.hash(state);
}
}
impl PartialEq for TypeReg {
fn eq(&self, other: &Self) -> bool {
self.type_id == other.type_id
}
}
impl Eq for TypeReg {}
const ID_UNSET: u32 = u32::MAX;
static TYPE_SET: FixedHashSet<TypeReg, MAX_TYPE_ID> = FixedHashSet::new();
static NEXT_TYPE_ID: AtomicUsize = AtomicUsize::new(0);
static SHADE_FN_TABLE: [AtomicPtr<()>; MAX_TYPE_ID] =
[const { AtomicPtr::new(std::ptr::null_mut()) }; MAX_TYPE_ID];
fn register_type<T: TraceObj>() -> u8 {
let reg = TypeReg {
type_id: TypeId::of::<T>(),
id: AtomicU32::new(ID_UNSET),
};
let (entry, inserted) = TYPE_SET.get_or_insert(reg);
if inserted {
cold_path();
let new_id = NEXT_TYPE_ID.fetch_add(1, Ordering::Relaxed);
assert!(
new_id < MAX_TYPE_ID,
"type registry overflow: more than 2^TYPE_ID_BITS distinct managed types \
(consider increasing TYPE_ID_BITS)"
);
let f: ShadePointeeFn = shade_erased::<T>;
SHADE_FN_TABLE[new_id].store(f as *mut (), Ordering::Release);
entry.id.store(new_id as u32, Ordering::Release);
new_id as u8
} else {
let id = entry.id.load(Ordering::Acquire);
if id == ID_UNSET {
cold_path();
loop {
let id = entry.id.load(Ordering::Acquire);
if id != ID_UNSET {
return id as u8;
}
std::hint::spin_loop();
}
}
id as u8
}
}
unsafe fn shade_erased<U: TraceObj>(mobj_ptr: *mut (), guard: &Guard) {
let mobj = unsafe { &*(mobj_ptr as *const ManObj<U>) };
if mobj.is_marked(guard) {
return;
}
guard.schedule_mark(mobj);
}
#[inline(always)]
fn get_shade_fn(type_id: u8) -> ShadePointeeFn {
let ptr = SHADE_FN_TABLE[type_id as usize].load(Ordering::Acquire);
debug_assert!(!ptr.is_null());
unsafe { std::mem::transmute(ptr) }
}
macro_rules! tag_fn {
() => {};
(@emit
[$($meta:tt)*] [$($kw:tt)*] $name:ident [$($sig:tt)*]
$body:block $($rest:tt)*
) => {
$($meta)*
#[cfg(feature = "tag")]
pub $($kw)* fn $name $($sig)* $body
$($meta)*
#[cfg(not(feature = "tag"))]
#[allow(dead_code)]
pub(crate) $($kw)* fn $name $($sig)* $body
tag_fn! { $($rest)* }
};
(@emit
[$($meta:tt)*] [$($kw:tt)*] $name:ident [$($sig:tt)*]
$tok:tt $($rest:tt)*
) => {
tag_fn! { @emit [$($meta)*] [$($kw)*] $name [$($sig)* $tok] $($rest)* }
};
($(#[$meta:meta])* const fn $name:ident $($rest:tt)*) => {
tag_fn! { @emit [$(#[$meta])*] [const] $name [] $($rest)* }
};
($(#[$meta:meta])* fn $name:ident $($rest:tt)*) => {
tag_fn! { @emit [$(#[$meta])*] [] $name [] $($rest)* }
};
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ObjMeta(usize);
impl From<usize> for ObjMeta {
#[inline(always)]
fn from(value: usize) -> Self {
Self(value)
}
}
impl ObjMeta {
const COLOR_SHIFT: u32 = 63;
const TYPE_ID_SHIFT: u32 = Self::COLOR_SHIFT - TYPE_ID_BITS;
const TYPE_ID_MASK: usize = ((1usize << TYPE_ID_BITS) - 1) << Self::TYPE_ID_SHIFT;
const ROOT_COUNT_BITS: u32 = Self::TYPE_ID_SHIFT;
const ROOT_COUNT_MASK: usize = (1usize << Self::ROOT_COUNT_BITS) - 1;
#[cfg(test)]
#[inline(always)]
pub fn new(marked: Color, root_count: usize) -> Self {
debug_assert!(root_count <= Self::ROOT_COUNT_MASK);
let bits = ((marked as usize) << Self::COLOR_SHIFT) | (root_count & Self::ROOT_COUNT_MASK);
Self(bits)
}
#[inline(always)]
pub fn new_with_type_id(marked: Color, root_count: usize, type_id: u8) -> Self {
debug_assert!(root_count <= Self::ROOT_COUNT_MASK);
let bits = ((marked as usize) << Self::COLOR_SHIFT)
| ((type_id as usize) << Self::TYPE_ID_SHIFT)
| (root_count & Self::ROOT_COUNT_MASK);
Self(bits)
}
#[inline(always)]
pub fn type_id(self) -> u8 {
((self.0 & Self::TYPE_ID_MASK) >> Self::TYPE_ID_SHIFT) as u8
}
#[inline(always)]
pub fn marked(self) -> Color {
(self.0 >> Self::COLOR_SHIFT).into()
}
#[inline(always)]
pub fn with_marked(self, color: Color) -> Self {
let cleared = self.0 & !(1usize << Self::COLOR_SHIFT);
Self(cleared | ((color as usize) << Self::COLOR_SHIFT))
}
#[inline(always)]
pub fn root_count(self) -> usize {
self.0 & Self::ROOT_COUNT_MASK
}
}
pub(crate) struct AtomicObjMeta(AtomicUsize);
impl Default for AtomicObjMeta {
#[inline(always)]
fn default() -> Self {
Self::from(ObjMeta::default())
}
}
impl AtomicObjMeta {
#[cfg(test)]
#[inline(always)]
pub fn new(marked: Color, root_count: usize) -> Self {
Self::from(ObjMeta::new(marked, root_count))
}
#[inline(always)]
pub fn load(&self, order: Ordering) -> ObjMeta {
ObjMeta::from(self.0.load(order))
}
#[inline(always)]
pub fn increment_root_count(&self, order: Ordering) -> usize {
let prev = ObjMeta::from(self.0.fetch_add(1, order)).root_count();
debug_assert!(prev < ObjMeta::ROOT_COUNT_MASK);
prev
}
#[inline(always)]
pub fn decrement_root_count(&self, order: Ordering) -> usize {
let prev = ObjMeta::from(self.0.fetch_sub(1, order)).root_count();
debug_assert!(prev > 0);
prev
}
#[inline(always)]
pub fn mark(&self, guard: &Guard) {
let mut meta = ObjMeta::from(self.0.load(Ordering::Relaxed));
while meta.marked() != guard.black_color() {
match self.0.compare_exchange(
meta.0,
meta.with_marked(guard.black_color()).0,
Ordering::AcqRel,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(current) => meta = ObjMeta::from(current),
}
}
}
}
impl From<ObjMeta> for AtomicObjMeta {
#[inline(always)]
fn from(value: ObjMeta) -> Self {
Self(AtomicUsize::new(value.0))
}
}
#[repr(C)]
pub(crate) struct ManObj<T> {
pub(crate) header: AtomicObjMeta,
item: T,
}
impl<T: TraceObj> ManObj<T> {
#[inline(always)]
pub fn new(item: T, color: Color, root_count: usize) -> Self {
let type_id = register_type::<T>();
Self {
header: AtomicObjMeta::from(ObjMeta::new_with_type_id(color, root_count, type_id)),
item,
}
}
#[inline(always)]
pub fn mark(&self, guard: &Guard) {
if !self.is_marked(guard) {
unsafe { self.shade_outgoings(guard) };
self.header.mark(guard);
}
}
#[inline(always)]
pub fn is_marked(&self, guard: &Guard) -> bool {
self.header.load(Ordering::Acquire).marked() == guard.black_color()
}
}
impl<T: TraceObj> TraceObj for ManObj<T> {
#[inline(always)]
unsafe fn unroot_outgoings(&self, guard: &Guard) {
unsafe { self.item.unroot_outgoings(guard) };
}
#[inline(always)]
unsafe fn shade_outgoings(&self, guard: &Guard) {
unsafe { self.item.shade_outgoings(guard) };
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PtrMeta {
Rooted,
Unrooted(Color),
}
pub struct ManPtr<T> {
data: *mut (),
_marker: PhantomData<*mut ManObj<T>>,
}
impl<T> Clone for ManPtr<T> {
#[inline(always)]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for ManPtr<T> {}
impl<T> PartialEq for ManPtr<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl<T> Eq for ManPtr<T> {}
impl<T> From<*mut ()> for ManPtr<T> {
#[inline(always)]
fn from(value: *mut ()) -> Self {
Self {
data: value,
_marker: PhantomData,
}
}
}
impl<T> From<NonNull<ManObj<T>>> for ManPtr<T> {
#[inline(always)]
fn from(value: NonNull<ManObj<T>>) -> Self {
Self {
data: value.cast().as_ptr(),
_marker: PhantomData,
}
}
}
impl<'g, G, T> From<Option<&Local<'g, G, T>>> for ManPtr<T>
where
G: Protector,
{
fn from(value: Option<&Local<'g, G, T>>) -> Self {
value.map(|l| l.as_man_ptr()).unwrap_or(Self::null_base())
}
}
impl<'g, G, T> From<WithTag<Option<&Local<'g, G, T>>>> for ManPtr<T>
where
G: Protector,
{
fn from(value: WithTag<Option<&Local<'g, G, T>>>) -> Self {
Self::from(value.0).with_tag(value.1)
}
}
impl<T> ManPtr<T> {
const META_WIDTH: u32 = 2;
const META_BITS: usize = ((1 << Self::META_WIDTH) - 1) << (usize::BITS - Self::META_WIDTH);
const LOW_BITS: usize = (1 << align_of::<ManObj<T>>().trailing_zeros()) - 1;
const ADDR_BITS: usize = usize::MAX & !Self::META_BITS & !Self::LOW_BITS;
#[inline(always)]
pub(crate) const fn null_base() -> Self {
Self {
data: null_mut(),
_marker: PhantomData,
}
}
#[inline(always)]
pub(crate) const fn null_rooted() -> Self {
Self::null_rooted_with_tag(0)
}
#[inline(always)]
pub(crate) const fn null_rooted_with_tag(tag: usize) -> Self {
Self {
data: (tag & Self::LOW_BITS) as *const () as *mut (),
_marker: PhantomData,
}
}
#[inline(always)]
pub(crate) fn meta(self) -> PtrMeta {
let bits = self.data.addr();
if bits & (1 << (usize::BITS - 1)) == 0 {
PtrMeta::Rooted
} else {
PtrMeta::Unrooted(Color::from(bits & (1 << (usize::BITS - 2))))
}
}
#[inline(always)]
pub(crate) fn with_meta(self, meta: PtrMeta) -> Self {
let new_ptr = self.data.map_addr(|addr| {
let wo_meta = addr & !Self::META_BITS;
let meta = match meta {
PtrMeta::Rooted => 0b00,
PtrMeta::Unrooted(Color::C0) => 0b10,
PtrMeta::Unrooted(Color::C1) => 0b11,
};
(meta << (usize::BITS - Self::META_WIDTH)) | wo_meta
});
Self {
data: new_ptr,
_marker: PhantomData,
}
}
#[inline(always)]
pub(crate) fn without_meta(self) -> Self {
Self {
data: self.data.map_addr(|addr| addr & !Self::META_BITS),
_marker: PhantomData,
}
}
#[inline(always)]
pub(crate) fn as_ptr(self) -> *mut ManObj<T> {
self.data.map_addr(|addr| addr & Self::ADDR_BITS).cast()
}
#[inline(always)]
pub(crate) fn tag(self) -> usize {
self.data.addr() & Self::LOW_BITS
}
#[inline(always)]
pub(crate) fn with_tag(self, tag: usize) -> Self {
Self {
data: self
.data
.map_addr(|addr| (addr & !Self::LOW_BITS) | (tag & Self::LOW_BITS)),
_marker: PhantomData,
}
}
#[inline(always)]
pub(crate) fn is_null(self) -> bool {
self.as_ptr().is_null()
}
#[inline(always)]
pub(crate) unsafe fn deref<'l>(self) -> &'l ManObj<T> {
unsafe { &*self.as_ptr() }
}
#[inline(always)]
pub(crate) unsafe fn unrooted_color_unchecked(self) -> Color {
Color::from(self.data.addr() & (1 << (usize::BITS - 2)))
}
#[inline(always)]
pub(crate) unsafe fn as_ref<'l>(self) -> Option<&'l ManObj<T>> {
unsafe { self.as_ptr().as_ref() }
}
}
impl<T: TraceObj> ManPtr<T> {
#[inline(always)]
pub(crate) fn alloc_rooted(item: T, color: Color, root_count: usize, guard: &Guard) -> Self {
debug_assert!(root_count > 0);
let obj = ManObj::new(item, color, root_count);
let addr = guard.alloc(obj);
let ptr = Self {
data: addr.cast(),
_marker: PhantomData,
};
ptr.with_meta(PtrMeta::Rooted)
}
#[inline(always)]
pub(crate) fn alloc_unrooted(item: T, color: Color, guard: &Guard) -> Self {
let obj = ManObj::new(item, color, 0);
let addr = guard.alloc(obj);
let ptr = Self {
data: addr.cast(),
_marker: PhantomData,
};
ptr.with_meta(PtrMeta::Unrooted(color))
}
#[inline(always)]
pub(crate) fn shade_pointee(self, guard: &Guard) -> bool {
let Some(mobj) = (unsafe { self.as_ref() }) else {
return false;
};
if mobj.is_marked(guard) {
return false;
}
guard.schedule_mark(mobj);
true
}
}
impl<T: TraceObj> ManPtr<T> {
#[inline(always)]
pub(crate) unsafe fn as_local_with_tag<'g>(
self,
guard: &'g Guard,
) -> WithTag<Option<Local<'g, Guard, T>>> {
let tag = self.tag();
let opt_local = if self.is_null() {
None
} else {
Some(unsafe { Local::from_raw(self.without_meta().with_tag(0), guard) })
};
(opt_local, tag)
}
}
pub trait TracePtr {
fn unroot(&self, guard: &Guard);
fn shade(&self, guard: &Guard);
}
pub trait TraceObj: 'static + Sync + Send {
unsafe fn unroot_outgoings(&self, guard: &Guard);
unsafe fn shade_outgoings(&self, guard: &Guard);
}
macro_rules! empty_trace_impl {
($($T:ty),*) => {
$(
impl TraceObj for $T {
#[inline]
unsafe fn unroot_outgoings(&self, _: &Guard) {}
#[inline]
unsafe fn shade_outgoings(&self, _: &Guard) {}
}
)*
}
}
empty_trace_impl![
(),
bool,
isize,
usize,
i8,
u8,
i16,
u16,
i32,
u32,
i64,
u64,
i128,
u128,
f32,
f64,
char,
String,
str,
std::path::Path,
std::path::PathBuf,
std::num::NonZeroIsize,
std::num::NonZeroUsize,
std::num::NonZeroI8,
std::num::NonZeroU8,
std::num::NonZeroI16,
std::num::NonZeroU16,
std::num::NonZeroI32,
std::num::NonZeroU32,
std::num::NonZeroI64,
std::num::NonZeroU64,
std::num::NonZeroI128,
std::num::NonZeroU128,
std::sync::atomic::AtomicBool,
std::sync::atomic::AtomicIsize,
std::sync::atomic::AtomicUsize,
std::sync::atomic::AtomicI8,
std::sync::atomic::AtomicU8,
std::sync::atomic::AtomicI16,
std::sync::atomic::AtomicU16,
std::sync::atomic::AtomicI32,
std::sync::atomic::AtomicU32,
std::sync::atomic::AtomicI64,
std::sync::atomic::AtomicU64,
std::collections::hash_map::DefaultHasher,
std::hash::RandomState
];
pub(crate) trait MarkObj: TraceObj {
fn mark(&self, guard: &Guard);
fn color(&self) -> Color;
fn root_count(&self) -> usize;
fn address(&self) -> *mut ();
}
impl<T: TraceObj> MarkObj for ManObj<T> {
#[inline(always)]
fn mark(&self, guard: &Guard) {
unsafe { self.shade_outgoings(guard) };
self.header.mark(guard);
}
#[inline(always)]
fn color(&self) -> Color {
self.header.load(Ordering::Relaxed).marked()
}
#[inline(always)]
fn root_count(&self) -> usize {
self.header.load(Ordering::Relaxed).root_count()
}
#[inline(always)]
fn address(&self) -> *mut () {
((&self.header) as *const _ as *const ()).cast_mut()
}
}
pub struct AtomicSharedOption<T> {
link: AtomicPtr<()>,
_marker: PhantomData<*mut T>,
}
assert_eq_size!(AtomicSharedOption<()>, usize);
const_assert!(ManPtr::<()>::null_rooted().data.is_null());
unsafe impl<T: Sync + Send> Sync for AtomicSharedOption<T> {}
unsafe impl<T: Sync + Send> Send for AtomicSharedOption<T> {}
impl<T> Default for AtomicSharedOption<T> {
#[inline(always)]
fn default() -> Self {
Self::none()
}
}
impl<T> AtomicSharedOption<T> {
#[inline(always)]
pub const fn none() -> Self {
Self {
link: AtomicPtr::new(null_mut()),
_marker: PhantomData,
}
}
tag_fn! {
#[inline(always)]
const fn none_with_tag(tag: usize) -> Self {
Self::from_raw(ManPtr::null_rooted_with_tag(tag))
}
}
#[inline(always)]
pub(crate) const fn from_raw(ptr: ManPtr<T>) -> Self {
Self {
link: AtomicPtr::new(ptr.data),
_marker: PhantomData,
}
}
}
impl<T: TraceObj> AtomicSharedOption<T> {
#[inline(always)]
pub fn some(item: T, guard: &Guard) -> Self {
let ptr = ManPtr::alloc_rooted(item, guard.alloc_color(), 1, guard);
unsafe { ptr.deref().item.unroot_outgoings(guard) };
Self::from_raw(ptr)
}
tag_fn! {
#[inline(always)]
fn some_with_tag(item: T, tag: usize, guard: &Guard) -> Self {
let ptr = ManPtr::alloc_rooted(item, guard.alloc_color(), 1, guard);
unsafe { ptr.deref().item.unroot_outgoings(guard) };
Self::from_raw(ptr.with_tag(tag))
}
}
#[inline(always)]
pub fn load<'g>(&self, order: Ordering, guard: &'g Guard) -> Option<Local<'g, Guard, T>> {
self.load_with_tag(order, guard).0
}
tag_fn! {
#[inline(always)]
fn load_with_tag<'g>(
&self,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Option<Local<'g, Guard, T>>> {
let ptr = ManPtr::from(self.link.load(order));
unsafe { ptr.as_local_with_tag(guard) }
}
}
pub fn store<'l, G: Protector>(
&self,
new: Option<&Local<'l, G, T>>,
order: Ordering,
guard: &Guard,
) {
self.store_with_tag(new, 0, order, guard);
}
tag_fn! {
fn store_with_tag<'l, G: Protector>(
&self,
new: Option<&Local<'l, G, T>>,
tag: usize,
order: Ordering,
guard: &Guard,
) {
self.swap_with_tag(new, tag, order, guard);
}
}
pub fn take<'g>(&self, order: Ordering, guard: &'g Guard) -> Option<Local<'g, Guard, T>> {
self.take_with_tag(order, guard).0
}
tag_fn! {
fn take_with_tag<'g>(
&self,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Option<Local<'g, Guard, T>>> {
self.swap_with_tag(None::<&Local<'g, Guard, T>>, 0, order, guard)
}
}
pub fn swap<'l, 'g, G: Protector>(
&self,
new: Option<&Local<'l, G, T>>,
order: Ordering,
guard: &'g Guard,
) -> Option<Local<'g, Guard, T>> {
self.swap_with_tag(new, 0, order, guard).0
}
tag_fn! {
fn swap_with_tag<'l, 'g, G: Protector>(
&self,
new: Option<&Local<'l, G, T>>,
tag: usize,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Option<Local<'g, Guard, T>>> {
let ptr = self.internal_swap(ManPtr::from(new).with_tag(tag), order, guard);
unsafe { ptr.as_local_with_tag(guard) }
}
}
fn internal_swap(&self, new: ManPtr<T>, order: Ordering, guard: &Guard) -> ManPtr<T> {
let mut old = ManPtr::<T>::from(self.link.load(Ordering::Relaxed));
if old.meta() == PtrMeta::Rooted {
cold_path();
let new_shared = Shared::try_inc_raw(new, guard);
let new_rooted = new.with_meta(PtrMeta::Rooted);
while old.meta() == PtrMeta::Rooted {
match self.internal_cmpxchg_rooted::<false>(
old,
new_rooted,
order,
Ordering::Relaxed,
guard,
) {
Ok(current) => {
forget(new_shared);
return current;
}
Err(current) => old = current,
}
}
}
loop {
match self.internal_cmpxchg_unrooted(old, new, order, Ordering::Relaxed, guard) {
Ok(current) => return current,
Err(current) => old = current,
}
}
}
pub fn compare_exchange<'l1, 'l2, 'g, G1, G2>(
&self,
current: Option<&Local<'l1, G1, T>>,
new: Option<&Local<'l2, G2, T>>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<Option<Local<'g, Guard, T>>>
where
G1: Protector,
G2: Protector,
{
self.compare_exchange_with_tag((current, 0), (new, 0), success, failure, guard)
.map(|pt| pt.0)
.map_err(|pt| pt.0)
}
tag_fn! {
fn compare_exchange_with_tag<'l1, 'l2, 'g, G1, G2>(
&self,
current_tag: WithTag<Option<&Local<'l1, G1, T>>>,
new_tag: WithTag<Option<&Local<'l2, G2, T>>>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<WithTag<Option<Local<'g, Guard, T>>>>
where
G1: Protector,
G2: Protector,
{
let mut old = ManPtr::<T>::from(self.link.load(Ordering::Relaxed));
if old.without_meta() != ManPtr::from(current_tag) {
return Err(unsafe { old.as_local_with_tag(guard) });
}
if old.meta() == PtrMeta::Rooted {
match self
.internal_cmpxchg_strong_rooted(old, current_tag, new_tag, success, failure, guard)
{
ControlFlow::Break(result) => return result,
ControlFlow::Continue(new_old) => old = new_old,
}
}
self.internal_cmpxchg_unrooted(old, ManPtr::from(new_tag), success, failure, guard)
.map(|current| unsafe { current.as_local_with_tag(guard) })
.map_err(|current| unsafe { current.as_local_with_tag(guard) })
}
}
#[cold]
#[inline(never)]
fn internal_cmpxchg_strong_rooted<'l1, 'l2, 'g, G1, G2>(
&self,
old: ManPtr<T>,
current_tag: WithTag<Option<&Local<'l1, G1, T>>>,
new_tag: WithTag<Option<&Local<'l2, G2, T>>>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> RootedCasFallback<'g, T>
where
G1: Protector,
G2: Protector,
{
let new_shared = Shared::try_inc_raw(ManPtr::from(new_tag.0), guard);
let new_rooted = ManPtr::from(new_tag).with_meta(PtrMeta::Rooted);
let old = match self
.internal_cmpxchg_rooted::<false>(old, new_rooted, success, failure, guard)
{
Ok(current) => {
forget(new_shared);
return ControlFlow::Break(Ok(unsafe { current.as_local_with_tag(guard) }));
}
Err(current) => match current.meta() {
PtrMeta::Rooted => {
return ControlFlow::Break(Err(unsafe { current.as_local_with_tag(guard) }));
}
PtrMeta::Unrooted(_) => current,
},
};
if old.without_meta() != ManPtr::from(current_tag) {
return ControlFlow::Break(Err(unsafe { old.as_local_with_tag(guard) }));
}
ControlFlow::Continue(old)
}
#[cold]
#[inline(never)]
fn internal_cmpxchg_weak_rooted<'l2, 'g, G2>(
&self,
old: ManPtr<T>,
new_tag: WithTag<Option<&Local<'l2, G2, T>>>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<WithTag<Option<Local<'g, Guard, T>>>>
where
G2: Protector,
{
let new_shared = Shared::try_inc_raw(ManPtr::from(new_tag.0), guard);
let new_rooted = ManPtr::from(new_tag).with_meta(PtrMeta::Rooted);
match self.internal_cmpxchg_rooted::<true>(old, new_rooted, success, failure, guard) {
Ok(current) => {
forget(new_shared);
Ok(unsafe { current.as_local_with_tag(guard) })
}
Err(current) => Err(unsafe { current.as_local_with_tag(guard) }),
}
}
pub fn compare_exchange_weak<'l1, 'l2, 'g, G1, G2>(
&self,
current: Option<&Local<'l1, G1, T>>,
new: Option<&Local<'l2, G2, T>>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<Option<Local<'g, Guard, T>>>
where
G1: Protector,
G2: Protector,
{
self.compare_exchange_weak_with_tag((current, 0), (new, 0), success, failure, guard)
.map(|pt| pt.0)
.map_err(|pt| pt.0)
}
tag_fn! {
#[inline(always)]
fn compare_exchange_weak_with_tag<'l1, 'l2, 'g, G1, G2>(
&self,
current_tag: WithTag<Option<&Local<'l1, G1, T>>>,
new_tag: WithTag<Option<&Local<'l2, G2, T>>>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<WithTag<Option<Local<'g, Guard, T>>>>
where
G1: Protector,
G2: Protector,
{
let old = ManPtr::<T>::from(self.link.load(Ordering::Relaxed));
if old.without_meta() != ManPtr::from(current_tag) {
return Err(unsafe { old.as_local_with_tag(guard) });
}
if old.meta() == PtrMeta::Rooted {
return self.internal_cmpxchg_weak_rooted(old, new_tag, success, failure, guard);
}
self.internal_cmpxchg_unrooted_once::<true>(
old,
ManPtr::from(new_tag),
success,
failure,
guard,
)
.map(|c| unsafe { c.as_local_with_tag(guard) })
.map_err(|c| unsafe { c.as_local_with_tag(guard) })
}
}
fn internal_cmpxchg_rooted<const WEAK: bool>(
&self,
old: ManPtr<T>,
new_rooted: ManPtr<T>,
success: Ordering,
failure: Ordering,
guard: &Guard,
) -> Result<ManPtr<T>, ManPtr<T>> {
debug_assert!(old.meta() == PtrMeta::Rooted);
debug_assert!(new_rooted.meta() == PtrMeta::Rooted);
let cas = if WEAK {
self.link
.compare_exchange_weak(old.data, new_rooted.data, success, failure)
} else {
self.link
.compare_exchange(old.data, new_rooted.data, success, failure)
};
match cas {
Ok(_) => {
let Some(old_ref) = (unsafe { old.as_ref() }) else {
return Ok(old);
};
if old_ref.header.decrement_root_count(Ordering::Relaxed) == 1
&& guard.global_phase_with_fence() != Phase::N
{
old.shade_pointee(guard);
}
Ok(old)
}
Err(current) => Err(ManPtr::from(current)),
}
}
#[inline(always)]
fn internal_cmpxchg_unrooted_once<const WEAK: bool>(
&self,
old: ManPtr<T>,
new: ManPtr<T>,
success: Ordering,
failure: Ordering,
guard: &Guard,
) -> Result<ManPtr<T>, ManPtr<T>> {
debug_assert!(old.meta() != PtrMeta::Rooted);
let old_color = unsafe { old.unrooted_color_unchecked() };
let ptrs_differ = old.as_ptr() != new.as_ptr();
if ptrs_differ && old_color == guard.black_color() {
Self::dijkstra_insertion_barrier(new, guard);
}
let new = new.with_meta(PtrMeta::Unrooted(old_color));
let cas = if WEAK {
self.link
.compare_exchange_weak(old.data, new.data, success, failure)
} else {
self.link
.compare_exchange(old.data, new.data, success, failure)
};
match cas {
Ok(_) => {
if ptrs_differ
&& old_color == guard.white_color()
&& guard.global_phase_with_fence() != Phase::N
{
Self::yuasa_deletion_barrier(old, guard);
}
Ok(old)
}
Err(current) => Err(ManPtr::from(current)),
}
}
fn internal_cmpxchg_unrooted(
&self,
mut old: ManPtr<T>,
new: ManPtr<T>,
success: Ordering,
failure: Ordering,
guard: &Guard,
) -> Result<ManPtr<T>, ManPtr<T>> {
loop {
match self.internal_cmpxchg_unrooted_once::<false>(old, new, success, failure, guard) {
Ok(o) => return Ok(o),
Err(current) => {
if current.without_meta() == old.without_meta() {
old = current;
continue;
}
return Err(current);
}
}
}
}
#[cold]
#[inline(never)]
fn dijkstra_insertion_barrier(new: ManPtr<T>, guard: &Guard) {
new.shade_pointee(guard);
}
#[cold]
#[inline(never)]
fn yuasa_deletion_barrier(old: ManPtr<T>, guard: &Guard) {
old.shade_pointee(guard);
}
tag_fn! {
#[inline(always)]
fn fetch_tag_and<'g>(
&self,
tag: usize,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Option<Local<'g, Guard, T>>> {
unsafe {
ManPtr::from(self.link.fetch_and(tag & ManPtr::<T>::LOW_BITS, order))
.as_local_with_tag(guard)
}
}
#[inline(always)]
fn fetch_tag_or<'g>(
&self,
tag: usize,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Option<Local<'g, Guard, T>>> {
unsafe {
ManPtr::from(self.link.fetch_or(tag & ManPtr::<T>::LOW_BITS, order))
.as_local_with_tag(guard)
}
}
#[inline(always)]
fn fetch_tag_xor<'g>(
&self,
tag: usize,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Option<Local<'g, Guard, T>>> {
unsafe {
ManPtr::from(self.link.fetch_xor(tag & ManPtr::<T>::LOW_BITS, order))
.as_local_with_tag(guard)
}
}
}
}
impl<T> Drop for AtomicSharedOption<T> {
#[inline(always)]
fn drop(&mut self) {
let ptr = ManPtr::<T>::from(self.link.load(Ordering::Relaxed));
if ptr.meta() != PtrMeta::Rooted {
return;
}
if ptr.is_null() {
return;
}
let guard = pin();
let header = unsafe { &*(ptr.as_ptr() as *const AtomicObjMeta) };
if header.decrement_root_count(Ordering::Relaxed) == 1
&& guard.global_phase_with_fence() != Phase::N
{
let meta = header.load(Ordering::Relaxed);
let shade_fn = get_shade_fn(meta.type_id());
unsafe { shade_fn(ptr.as_ptr() as *mut (), &guard) };
}
}
}
impl<T: TraceObj> TracePtr for AtomicSharedOption<T> {
#[inline(always)]
fn unroot(&self, guard: &Guard) {
let ptr = ManPtr::<T>::from(self.link.load(Ordering::Relaxed));
debug_assert!(ptr.meta() == PtrMeta::Rooted);
if let Some(obj) = unsafe { ptr.as_ref() } {
cold_path(); let count = obj.header.decrement_root_count(Ordering::Relaxed);
if count == 1 && guard.global_phase_with_fence() != Phase::N {
ptr.shade_pointee(guard);
}
}
self.link.store(
ptr.with_meta(PtrMeta::Unrooted(guard.alloc_color())).data,
Ordering::Relaxed,
);
}
#[inline(always)]
fn shade(&self, guard: &Guard) {
let mut ptr = ManPtr::<T>::from(self.link.load(Ordering::Relaxed));
loop {
debug_assert!(matches!(ptr.meta(), PtrMeta::Unrooted(_)));
if ptr.meta() == PtrMeta::Unrooted(guard.black_color()) {
break;
}
ptr.shade_pointee(guard);
let black = ptr.with_meta(PtrMeta::Unrooted(guard.black_color()));
match self.link.compare_exchange(
ptr.data,
black.data,
Ordering::AcqRel,
Ordering::Relaxed,
) {
Ok(_) => break,
Err(current) => ptr = ManPtr::<T>::from(current),
}
}
}
}
impl<T> From<AtomicShared<T>> for AtomicSharedOption<T> {
fn from(value: AtomicShared<T>) -> Self {
value.inner
}
}
impl<T> From<Shared<T>> for AtomicSharedOption<T> {
fn from(value: Shared<T>) -> Self {
value.inner.inner
}
}
impl<T> From<Option<Shared<T>>> for AtomicSharedOption<T> {
fn from(value: Option<Shared<T>>) -> Self {
if let Some(shared) = value {
Self::from(shared)
} else {
AtomicSharedOption::none()
}
}
}
impl<T: TraceObj> From<Option<&Shared<T>>> for AtomicSharedOption<T> {
fn from(value: Option<&Shared<T>>) -> Self {
if let Some(shared) = value {
Self::from(shared.clone())
} else {
AtomicSharedOption::none()
}
}
}
impl<'g, G, T> From<Local<'g, G, T>> for AtomicSharedOption<T>
where
G: Protector,
{
fn from(value: Local<'g, G, T>) -> Self {
Self::from(value.as_atomic_shared())
}
}
impl<'g, G, T> From<Option<Local<'g, G, T>>> for AtomicSharedOption<T>
where
G: Protector,
{
fn from(value: Option<Local<'g, G, T>>) -> Self {
if let Some(local) = value {
Self::from(local)
} else {
AtomicSharedOption::none()
}
}
}
impl<'g, G, T> From<Option<&Local<'g, G, T>>> for AtomicSharedOption<T>
where
G: Protector,
{
fn from(value: Option<&Local<'g, G, T>>) -> Self {
if let Some(local) = value {
Self::from(local.as_atomic_shared())
} else {
AtomicSharedOption::none()
}
}
}
pub struct AtomicShared<T> {
inner: AtomicSharedOption<T>,
}
unsafe impl<T: Sync + Send> Sync for AtomicShared<T> {}
unsafe impl<T: Sync + Send> Send for AtomicShared<T> {}
impl<T> AtomicShared<T> {
#[inline(always)]
pub(crate) const fn from_raw(ptr: ManPtr<T>) -> Self {
Self {
inner: AtomicSharedOption::from_raw(ptr),
}
}
}
impl<T: TraceObj> AtomicShared<T> {
#[inline(always)]
pub fn new(item: T, guard: &Guard) -> Self {
Self {
inner: AtomicSharedOption::some(item, guard),
}
}
tag_fn! {
#[inline(always)]
fn new_with_tag(item: T, tag: usize, guard: &Guard) -> Self {
Self {
inner: AtomicSharedOption::some_with_tag(item, tag, guard),
}
}
}
#[inline(always)]
pub fn load<'g>(&self, order: Ordering, guard: &'g Guard) -> Local<'g, Guard, T> {
let r = self.inner.load(order, guard);
debug_assert!(r.is_some());
unsafe { r.unwrap_unchecked() }
}
tag_fn! {
#[inline(always)]
fn load_with_tag<'g>(
&self,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Local<'g, Guard, T>> {
let r = self.inner.load_with_tag(order, guard);
debug_assert!(r.0.is_some());
unsafe { (r.0.unwrap_unchecked(), r.1) }
}
}
pub fn store<'l, G: Protector>(&self, new: &Local<'l, G, T>, order: Ordering, guard: &Guard) {
self.inner.store(Some(new), order, guard);
}
tag_fn! {
fn store_with_tag<'l, G: Protector>(
&self,
new: &Local<'l, G, T>,
tag: usize,
order: Ordering,
guard: &Guard,
) {
self.inner.store_with_tag(Some(new), tag, order, guard);
}
}
pub fn swap<'l, 'g, G: Protector>(
&self,
new: &Local<'l, G, T>,
order: Ordering,
guard: &'g Guard,
) -> Local<'g, Guard, T> {
let r = self.inner.swap(Some(new), order, guard);
debug_assert!(r.is_some());
unsafe { r.unwrap_unchecked() }
}
tag_fn! {
fn swap_with_tag<'l, 'g, G: Protector>(
&self,
new: &Local<'l, G, T>,
tag: usize,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Local<'g, Guard, T>> {
let r = self.inner.swap_with_tag(Some(new), tag, order, guard);
debug_assert!(r.0.is_some());
unsafe { (r.0.unwrap_unchecked(), r.1) }
}
}
pub fn compare_exchange<'l1, 'l2, 'g, G1, G2>(
&self,
current: &Local<'l1, G1, T>,
new: &Local<'l2, G2, T>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<Local<'g, Guard, T>>
where
G1: Protector,
G2: Protector,
{
let r = self
.inner
.compare_exchange(Some(current), Some(new), success, failure, guard);
match r {
Ok(l) | Err(l) => debug_assert!(l.is_some()),
}
unsafe {
r.map(|l| l.unwrap_unchecked())
.map_err(|l| l.unwrap_unchecked())
}
}
tag_fn! {
fn compare_exchange_with_tag<'l1, 'l2, 'g, G1, G2>(
&self,
current_tag: WithTag<&Local<'l1, G1, T>>,
new_tag: WithTag<&Local<'l2, G2, T>>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<WithTag<Local<'g, Guard, T>>>
where
G1: Protector,
G2: Protector,
{
let r = self.inner.compare_exchange_with_tag(
(Some(current_tag.0), current_tag.1),
(Some(new_tag.0), new_tag.1),
success,
failure,
guard,
);
match r {
Ok((l, _)) | Err((l, _)) => debug_assert!(l.is_some()),
}
unsafe {
r.map(|(l, t)| (l.unwrap_unchecked(), t))
.map_err(|(l, t)| (l.unwrap_unchecked(), t))
}
}
}
pub fn compare_exchange_weak<'l1, 'l2, 'g, G1, G2>(
&self,
current: &Local<'l1, G1, T>,
new: &Local<'l2, G2, T>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<Local<'g, Guard, T>>
where
G1: Protector,
G2: Protector,
{
let r = self
.inner
.compare_exchange_weak(Some(current), Some(new), success, failure, guard);
match r {
Ok(l) | Err(l) => debug_assert!(l.is_some()),
}
unsafe {
r.map(|l| l.unwrap_unchecked())
.map_err(|l| l.unwrap_unchecked())
}
}
tag_fn! {
fn compare_exchange_weak_with_tag<'l1, 'l2, 'g, G1, G2>(
&self,
current_tag: WithTag<&Local<'l1, G1, T>>,
new_tag: WithTag<&Local<'l2, G2, T>>,
success: Ordering,
failure: Ordering,
guard: &'g Guard,
) -> CasResult<WithTag<Local<'g, Guard, T>>>
where
G1: Protector,
G2: Protector,
{
let r = self.inner.compare_exchange_weak_with_tag(
(Some(current_tag.0), current_tag.1),
(Some(new_tag.0), new_tag.1),
success,
failure,
guard,
);
match r {
Ok((l, _)) | Err((l, _)) => debug_assert!(l.is_some()),
}
unsafe {
r.map(|(l, t)| (l.unwrap_unchecked(), t))
.map_err(|(l, t)| (l.unwrap_unchecked(), t))
}
}
}
tag_fn! {
#[inline(always)]
fn fetch_tag_and<'g>(
&self,
tag: usize,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Local<'g, Guard, T>> {
let r = self.inner.fetch_tag_and(tag, order, guard);
debug_assert!(r.0.is_some());
unsafe { (r.0.unwrap_unchecked(), r.1) }
}
#[inline(always)]
fn fetch_tag_or<'g>(
&self,
tag: usize,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Local<'g, Guard, T>> {
let r = self.inner.fetch_tag_or(tag, order, guard);
debug_assert!(r.0.is_some());
unsafe { (r.0.unwrap_unchecked(), r.1) }
}
#[inline(always)]
fn fetch_tag_xor<'g>(
&self,
tag: usize,
order: Ordering,
guard: &'g Guard,
) -> WithTag<Local<'g, Guard, T>> {
let r = self.inner.fetch_tag_xor(tag, order, guard);
debug_assert!(r.0.is_some());
unsafe { (r.0.unwrap_unchecked(), r.1) }
}
}
}
impl<T: TraceObj> TracePtr for AtomicShared<T> {
#[inline(always)]
fn unroot(&self, guard: &Guard) {
self.inner.unroot(guard);
}
#[inline(always)]
fn shade(&self, guard: &Guard) {
self.inner.shade(guard);
}
}
impl<T> From<Shared<T>> for AtomicShared<T> {
fn from(value: Shared<T>) -> Self {
value.inner
}
}
impl<'g, G, T> From<Local<'g, G, T>> for AtomicShared<T>
where
G: Protector,
{
fn from(value: Local<'g, G, T>) -> Self {
value.as_atomic_shared()
}
}
pub struct Shared<T> {
inner: AtomicShared<T>,
}
assert_eq_size!(Shared<()>, usize);
unsafe impl<T: Sync + Send> Sync for Shared<T> {}
unsafe impl<T: Sync + Send> Send for Shared<T> {}
impl<T> Shared<T> {
#[inline(always)]
pub(crate) fn as_man_ptr(&self) -> ManPtr<T> {
ManPtr::from(self.inner.inner.link.load(Ordering::Relaxed))
}
#[inline(always)]
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
this.as_man_ptr().without_meta() == other.as_man_ptr().without_meta()
}
#[inline(always)]
pub fn opt_ptr_eq(this: Option<&Self>, other: Option<&Self>) -> bool {
match (this, other) {
(None, None) => true,
(Some(l1), Some(l2)) => Self::ptr_eq(l1, l2),
_ => false,
}
}
}
impl<T: TraceObj> Shared<T> {
#[inline(always)]
pub fn new(item: T, guard: &Guard) -> Self {
Self {
inner: AtomicShared::new(item, guard),
}
}
pub(crate) fn try_inc_raw(ptr: ManPtr<T>, _: &Guard) -> Option<Self> {
let obj = (unsafe { ptr.as_ref() })?;
obj.header.increment_root_count(Ordering::Release);
Some(Self {
inner: AtomicShared::from_raw(ptr.with_meta(PtrMeta::Rooted)),
})
}
#[inline(always)]
pub fn as_local<'g>(&self, guard: &'g Guard) -> Local<'g, Guard, T> {
unsafe { Local::from_raw(self.as_man_ptr(), guard) }
}
}
impl<T> Clone for Shared<T> {
fn clone(&self) -> Self {
let ptr = ManPtr::from(self.inner.inner.link.load(Ordering::Relaxed));
debug_assert!(!ptr.is_null());
let obj = unsafe { ptr.deref() };
obj.header.increment_root_count(Ordering::Release);
Self {
inner: AtomicShared::from_raw(ptr.with_meta(PtrMeta::Rooted)),
}
}
}
impl<T> Deref for Shared<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe {
let r = self.as_man_ptr().as_ref().map(|obj| &obj.item);
debug_assert!(r.is_some());
r.unwrap_unchecked()
}
}
}
impl<T> AsRef<T> for Shared<T> {
fn as_ref(&self) -> &T {
self.deref()
}
}
impl<T> Borrow<T> for Shared<T> {
fn borrow(&self) -> &T {
self.deref()
}
}
impl<T: PartialEq> PartialEq for Shared<T> {
fn eq(&self, other: &Self) -> bool {
self.deref() == other.deref()
}
}
impl<T: Eq> Eq for Shared<T> {}
impl<T: PartialOrd> PartialOrd for Shared<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.deref().partial_cmp(other.deref())
}
}
impl<T: Ord> Ord for Shared<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.deref().cmp(other.deref())
}
}
impl<T: Hash> Hash for Shared<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.deref().hash(state);
}
}
impl<T: TraceObj> TracePtr for Shared<T> {
#[inline(always)]
fn unroot(&self, guard: &Guard) {
self.inner.unroot(guard);
}
#[inline(always)]
fn shade(&self, guard: &Guard) {
self.inner.shade(guard);
}
}
pub trait Protector {
type Shield;
fn protect<T: TraceObj>(&self, ptr: ManPtr<T>) -> Self::Shield;
}
impl Protector for Handle {
type Shield = HazardPointer;
#[inline(always)]
fn protect<T: TraceObj>(&self, ptr: ManPtr<T>) -> Self::Shield {
let hp = HazardPointer::new(self.local.clone());
hp.protect(ptr);
hp
}
}
impl Protector for Guard {
type Shield = ();
#[inline(always)]
fn protect<T: TraceObj>(&self, _: ManPtr<T>) -> Self::Shield {}
}
pub struct Local<'g, G: Protector, T> {
ptr: NonNull<ManObj<T>>,
sh: G::Shield,
_marker: PhantomData<(&'g (), ManPtr<T>)>,
}
assert_eq_size!(Local<'static, Guard, ()>, usize);
assert_eq_size!(Option<Local<'static, Guard, ()>>, usize);
unsafe impl<'g, G: Protector, T: Sync + Send> Sync for Local<'g, G, T> {}
impl<'g, T: TraceObj> Local<'g, Guard, T> {
#[inline(always)]
pub fn new(item: T, guard: &'g Guard) -> Self {
let ptr = ManPtr::alloc_unrooted(item, guard.alloc_color(), guard).without_meta();
let (ptr, sh) = unsafe {
ptr.deref().item.unroot_outgoings(guard);
(NonNull::new_unchecked(ptr.as_ptr()), guard.protect(ptr))
};
Self {
ptr,
sh,
_marker: PhantomData,
}
}
}
impl<'g, G: Protector, T> Local<'g, G, T> {
#[inline(always)]
pub(crate) fn as_man_ptr(&self) -> ManPtr<T> {
debug_assert!(ManPtr::<T>::from(self.ptr) == ManPtr::<T>::from(self.ptr).without_meta());
ManPtr::from(self.ptr)
}
#[inline(always)]
pub fn as_atomic_shared(&self) -> AtomicShared<T> {
let ptr = self.as_man_ptr();
debug_assert!(!ptr.is_null());
unsafe { ptr.deref() }
.header
.increment_root_count(Ordering::Release);
AtomicShared::from_raw(ptr.with_meta(PtrMeta::Rooted))
}
#[inline(always)]
pub fn as_shared(&self) -> Shared<T> {
Shared {
inner: self.as_atomic_shared(),
}
}
#[inline(always)]
pub fn ptr_eq<'h, H: Protector>(this: &Self, other: &Local<'h, H, T>) -> bool {
this.ptr == other.ptr
}
#[inline(always)]
pub fn opt_ptr_eq<'h, H: Protector>(
this: Option<&Self>,
other: Option<&Local<'h, H, T>>,
) -> bool {
match (this, other) {
(None, None) => true,
(Some(l1), Some(l2)) => Self::ptr_eq(l1, l2),
_ => false,
}
}
}
impl<'g, G: Protector, T: TraceObj> Local<'g, G, T> {
#[inline(always)]
pub(crate) unsafe fn from_raw(ptr: ManPtr<T>, prot: &G) -> Self {
debug_assert!(!ptr.is_null());
Self {
ptr: unsafe { NonNull::new_unchecked(ptr.as_ptr()) },
sh: prot.protect(ptr),
_marker: PhantomData,
}
}
#[inline(always)]
pub fn protect<'h, H: Protector>(&self, prot: &'h H) -> Local<'h, H, T> {
Local {
ptr: self.ptr,
sh: prot.protect(self.as_man_ptr()),
_marker: PhantomData,
}
}
}
impl<'g, G: Protector, T> Deref for Local<'g, G, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe {
let r = self.as_man_ptr().as_ref().map(|obj| &obj.item);
debug_assert!(r.is_some());
r.unwrap_unchecked()
}
}
}
impl<'g, G: Protector, T> AsRef<T> for Local<'g, G, T> {
fn as_ref(&self) -> &T {
self.deref()
}
}
impl<'g, G: Protector, T> Borrow<T> for Local<'g, G, T> {
fn borrow(&self) -> &T {
self.deref()
}
}
impl<'g, G, T> PartialEq for Local<'g, G, T>
where
G: Protector,
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<'g, G, T> Eq for Local<'g, G, T>
where
G: Protector,
T: Eq,
{
}
impl<'g, G, T> std::fmt::Debug for Local<'g, G, T>
where
G: Protector,
T: std::fmt::Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&**self, f)
}
}
impl<'g, G, T> PartialOrd for Local<'g, G, T>
where
G: Protector,
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<'g, G, T> Ord for Local<'g, G, T>
where
G: Protector,
T: Ord,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(**self).cmp(&**other)
}
}
impl<'g, G: Protector, T> Clone for Local<'g, G, T>
where
G::Shield: Clone,
{
#[inline(always)]
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
sh: self.sh.clone(),
_marker: PhantomData,
}
}
}
impl<'g, G: Protector, T> Copy for Local<'g, G, T> where G::Shield: Copy {}
#[cfg(test)]
mod tests {
use super::*;
use crate::epoch::Color;
#[test]
fn obj_meta_new_and_accessors() {
let meta = ObjMeta::new(Color::C0, 0);
assert_eq!(meta.marked(), Color::C0);
assert_eq!(meta.root_count(), 0);
let meta = ObjMeta::new(Color::C1, 5);
assert_eq!(meta.marked(), Color::C1);
assert_eq!(meta.root_count(), 5);
}
#[test]
fn obj_meta_with_marked() {
let meta = ObjMeta::new(Color::C0, 10);
let flipped = meta.with_marked(Color::C1);
assert_eq!(flipped.marked(), Color::C1);
assert_eq!(flipped.root_count(), 10);
}
#[test]
fn obj_meta_default() {
let meta = ObjMeta::default();
assert_eq!(meta.marked(), Color::C0);
assert_eq!(meta.root_count(), 0);
}
#[test]
fn atomic_obj_meta_load() {
let ameta = AtomicObjMeta::new(Color::C1, 3);
let loaded = ameta.load(Ordering::SeqCst);
assert_eq!(loaded.marked(), Color::C1);
assert_eq!(loaded.root_count(), 3);
}
#[test]
fn atomic_obj_meta_increment_root_count() {
let ameta = AtomicObjMeta::new(Color::C0, 1);
let prev = ameta.increment_root_count(Ordering::SeqCst);
assert_eq!(prev, 1);
assert_eq!(ameta.load(Ordering::SeqCst).root_count(), 2);
}
#[test]
fn atomic_obj_meta_decrement_root_count() {
let ameta = AtomicObjMeta::new(Color::C0, 3);
let prev = ameta.decrement_root_count(Ordering::SeqCst);
assert_eq!(prev, 3);
assert_eq!(ameta.load(Ordering::SeqCst).root_count(), 2);
}
#[test]
fn atomic_obj_meta_increment_preserves_color() {
let ameta = AtomicObjMeta::new(Color::C1, 5);
ameta.increment_root_count(Ordering::SeqCst);
let loaded = ameta.load(Ordering::SeqCst);
assert_eq!(loaded.marked(), Color::C1);
assert_eq!(loaded.root_count(), 6);
}
#[test]
fn ptr_meta_equality() {
assert_eq!(PtrMeta::Rooted, PtrMeta::Rooted);
assert_eq!(PtrMeta::Unrooted(Color::C0), PtrMeta::Unrooted(Color::C0));
assert_eq!(PtrMeta::Unrooted(Color::C1), PtrMeta::Unrooted(Color::C1));
assert_ne!(PtrMeta::Rooted, PtrMeta::Unrooted(Color::C0));
assert_ne!(PtrMeta::Unrooted(Color::C0), PtrMeta::Unrooted(Color::C1));
}
#[test]
fn man_ptr_null_base_is_null() {
let ptr = ManPtr::<()>::null_base();
assert!(ptr.is_null());
}
#[test]
fn man_ptr_null_rooted() {
let ptr = ManPtr::<()>::null_rooted();
assert!(ptr.is_null());
assert_eq!(ptr.meta(), PtrMeta::Rooted);
}
#[test]
fn man_ptr_null_rooted_with_tag() {
let ptr = ManPtr::<()>::null_rooted_with_tag(1);
assert!(ptr.is_null());
assert_eq!(ptr.meta(), PtrMeta::Rooted);
assert_eq!(ptr.tag(), 1);
}
#[test]
fn man_ptr_meta_roundtrip() {
let base = ManPtr::<()>::null_base();
let rooted = base.with_meta(PtrMeta::Rooted);
assert_eq!(rooted.meta(), PtrMeta::Rooted);
let c0 = base.with_meta(PtrMeta::Unrooted(Color::C0));
assert_eq!(c0.meta(), PtrMeta::Unrooted(Color::C0));
let c1 = base.with_meta(PtrMeta::Unrooted(Color::C1));
assert_eq!(c1.meta(), PtrMeta::Unrooted(Color::C1));
}
#[test]
fn man_ptr_without_meta() {
let base = ManPtr::<()>::null_base();
let rooted = base.with_meta(PtrMeta::Rooted);
let cleared = rooted.without_meta();
assert_eq!(cleared.meta(), PtrMeta::Rooted);
}
#[test]
fn man_ptr_tag_roundtrip() {
let ptr = ManPtr::<()>::null_rooted();
assert_eq!(ptr.tag(), 0);
let tagged = ptr.with_tag(1);
assert_eq!(tagged.tag(), 1);
}
#[test]
fn man_ptr_tag_preserves_meta() {
let ptr = ManPtr::<()>::null_rooted();
let tagged = ptr.with_tag(1);
assert_eq!(tagged.meta(), PtrMeta::Rooted);
}
#[test]
fn man_ptr_equality() {
let a = ManPtr::<()>::null_base();
let b = ManPtr::<()>::null_base();
assert!(a == b);
let c = a.with_meta(PtrMeta::Rooted);
assert!(a == c);
let d = a.with_meta(PtrMeta::Unrooted(Color::C0));
assert!(a != d);
}
#[test]
fn man_ptr_clone_copy() {
let ptr = ManPtr::<()>::null_rooted();
let cloned = ptr.clone();
assert!(ptr == cloned);
let copied = ptr;
assert!(ptr == copied);
}
}