use core::cell::Cell;
use core::marker::PhantomData;
use core::sync::atomic;
#[cfg(feature = "track_threads")]
use core::sync::atomic::AtomicUsize;
use core::sync::atomic::{AtomicU32, Ordering};
use static_assertions::{assert_eq_align, assert_eq_size, assert_impl_all, assert_not_impl_any};
use crate::algorithm::abort;
#[cfg(feature = "track_threads")]
use crate::algorithm::hybrid_threads::THREAD_ID;
use crate::{Algorithm, FlexRc, FlexRcInner};
#[cfg(not(feature = "track_threads"))]
assert_eq_size!(HybridMeta<LocalMode>, u64);
#[cfg(not(feature = "track_threads"))]
assert_eq_size!(HybridMeta<SharedMode>, u64);
assert_eq_size!(HybridMeta<LocalMode>, HybridMeta<SharedMode>);
assert_eq_align!(HybridMeta<LocalMode>, HybridMeta<SharedMode>);
assert_eq_size!(LocalInner<usize>, SharedInner<usize>);
assert_eq_align!(LocalInner<usize>, SharedInner<usize>);
assert_eq_size!(LocalHybridRc<usize>, SharedHybridRc<usize>);
assert_eq_align!(LocalHybridRc<usize>, SharedHybridRc<usize>);
assert_impl_all!(SharedHybridRc<usize>: Send, Sync);
assert_not_impl_any!(LocalHybridRc<usize>: Send, Sync);
#[cfg(feature = "track_threads")]
const THREAD_ID_LOCKED: usize = (usize::MAX >> 1) + 1;
#[cfg(feature = "track_threads")]
const THREAD_ID_UNLOCKED: usize = usize::MAX >> 1;
const MAX_LOCAL_COUNT: u32 = u32::MAX;
const MAX_SHARED_COUNT: u32 = u32::MAX >> 2;
const LOCAL_PRESENT: u32 = (u32::MAX >> 1) + 1;
const CLEAR_LOCAL: u32 = u32::MAX >> 1;
pub struct LocalMode;
pub struct SharedMode;
#[repr(C)]
pub struct HybridMeta<MODE> {
#[cfg(feature = "track_threads")]
thread_id: AtomicUsize,
local_count: Cell<u32>,
shared_count: AtomicU32,
phantom: PhantomData<MODE>,
}
pub type LocalHybridRc<T> = FlexRc<HybridMeta<LocalMode>, HybridMeta<SharedMode>, T>;
type LocalInner<T> = FlexRcInner<HybridMeta<LocalMode>, HybridMeta<SharedMode>, T>;
type SharedInner<T> = FlexRcInner<HybridMeta<SharedMode>, HybridMeta<LocalMode>, T>;
impl Algorithm<HybridMeta<LocalMode>, HybridMeta<SharedMode>> for HybridMeta<LocalMode> {
#[inline]
fn create() -> Self {
Self {
#[cfg(feature = "track_threads")]
thread_id: AtomicUsize::new(THREAD_ID.with(|t| t.0)),
local_count: Cell::new(1),
shared_count: AtomicU32::new(LOCAL_PRESENT),
phantom: PhantomData,
}
}
#[inline]
fn is_unique(&self) -> bool {
self.local_count.get() == 1 && self.shared_count.load(Ordering::Acquire) == LOCAL_PRESENT
}
#[inline(always)]
fn clone(&self) {
let old = self.local_count.get();
if old == MAX_LOCAL_COUNT {
abort()
}
self.local_count.set(old + 1);
}
#[inline(always)]
fn drop(&self) -> bool {
self.local_count.set(self.local_count.get() - 1);
if self.local_count.get() == 0 {
let old = self.shared_count.fetch_and(CLEAR_LOCAL, Ordering::Release);
old == LOCAL_PRESENT
} else {
false
}
}
#[inline]
fn try_into_other<T: ?Sized>(
&self,
inner: *mut LocalInner<T>,
) -> Result<*mut SharedInner<T>, *mut LocalInner<T>> {
let inner = inner as *mut SharedInner<T>;
unsafe {
(*inner).metadata.clone();
}
Ok(inner)
}
#[inline]
fn try_to_other<T: ?Sized>(
&self,
inner: *mut LocalInner<T>,
) -> Result<*mut SharedInner<T>, *mut LocalInner<T>> {
self.try_into_other(inner)
}
}
pub type SharedHybridRc<T> = FlexRc<HybridMeta<SharedMode>, HybridMeta<LocalMode>, T>;
unsafe impl<T: Send + Sync> Send for SharedHybridRc<T> {}
unsafe impl<T: Send + Sync> Sync for SharedHybridRc<T> {}
impl Algorithm<HybridMeta<SharedMode>, HybridMeta<LocalMode>> for HybridMeta<SharedMode> {
#[inline]
fn create() -> Self {
Self {
#[cfg(feature = "track_threads")]
thread_id: AtomicUsize::new(0),
local_count: Cell::new(0),
shared_count: AtomicU32::new(1),
phantom: PhantomData,
}
}
#[inline]
fn is_unique(&self) -> bool {
self.shared_count.load(Ordering::Acquire) == 1
}
#[inline(always)]
fn clone(&self) {
let old = self.shared_count.fetch_add(1, Ordering::Relaxed);
if old > MAX_SHARED_COUNT {
abort()
}
}
#[inline(always)]
fn drop(&self) -> bool {
if self.shared_count.fetch_sub(1, Ordering::Release) == 1 {
atomic::fence(Ordering::Acquire);
true
} else {
false
}
}
#[cfg(feature = "track_threads")]
#[inline]
fn try_into_other<T: ?Sized>(
&self,
inner: *mut SharedInner<T>,
) -> Result<*mut LocalInner<T>, *mut SharedInner<T>> {
let thread_id = THREAD_ID.with(|thread_id| thread_id.0);
let old_thread_id = loop {
let old_thread_id = self.thread_id.fetch_or(THREAD_ID_LOCKED, Ordering::Acquire);
if old_thread_id < THREAD_ID_LOCKED {
break old_thread_id;
}
std::hint::spin_loop();
};
let old_shared_count = self.shared_count.fetch_or(LOCAL_PRESENT, Ordering::Acquire);
if thread_id == old_thread_id || old_shared_count < LOCAL_PRESENT {
self.thread_id.store(thread_id, Ordering::Release);
let inner = inner as *mut LocalInner<T>;
unsafe {
(*inner).metadata.clone();
}
Ok(inner)
} else {
self.thread_id
.fetch_and(THREAD_ID_UNLOCKED, Ordering::Release);
Err(inner)
}
}
#[cfg(not(feature = "track_threads"))]
#[inline]
fn try_into_other<T: ?Sized>(
&self,
inner: *mut SharedInner<T>,
) -> Result<*mut LocalInner<T>, *mut SharedInner<T>> {
if self.shared_count.fetch_or(LOCAL_PRESENT, Ordering::Acquire) < LOCAL_PRESENT {
let inner = inner as *mut LocalInner<T>;
unsafe {
(*inner).metadata.clone();
}
Ok(inner)
} else {
Err(inner)
}
}
#[inline]
fn try_to_other<T: ?Sized>(
&self,
inner: *mut SharedInner<T>,
) -> Result<*mut LocalInner<T>, *mut SharedInner<T>> {
self.try_into_other(inner)
}
}