use core::{
cmp, fmt,
hash::{Hash, Hasher},
marker::PhantomData,
ops::Deref,
ptr,
sync::atomic,
};
#[cfg(cas_atomic_polyfill)]
use atomic_polyfill::{AtomicUsize, Ordering};
#[cfg(not(cas_atomic_polyfill))]
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::pool::{self, stack::Ptr, Node};
#[cfg(any(
armv6m,
armv7a,
armv7r,
armv7m,
armv8m_main,
all(
any(target_arch = "x86_64", target_arch = "x86"),
feature = "x86-sync-pool"
),
test
))]
#[macro_export]
macro_rules! arc_pool {
($(#[$($attr:tt)*])* $ident:ident: $ty:ty) => {
pub struct $ident;
impl $crate::pool::singleton::arc::Pool for $ident {
type Data = $ty;
fn ptr() -> &'static $crate::pool::Pool<$crate::pool::singleton::arc::ArcInner<$ty>> {
$(#[$($attr)*])*
static POOL: $crate::pool::Pool<$crate::pool::singleton::arc::ArcInner<$ty>> =
$crate::pool::Pool::new();
&POOL
}
}
impl $ident {
pub fn alloc(data: $ty) -> Result<$crate::Arc<Self>, $ty>
where
Self: Sized,
{
$crate::Arc::new(data)
}
pub fn grow(memory: &'static mut [u8]) -> usize {
<Self as $crate::pool::singleton::arc::Pool>::ptr().grow(memory)
}
pub fn grow_exact<A>(memory: &'static mut MaybeUninit<A>) -> usize
where
A: AsMut<[$crate::pool::Node<$crate::pool::singleton::arc::ArcInner<$ty>>]>,
{
<Self as $crate::pool::singleton::arc::Pool>::ptr().grow_exact(memory)
}
}
};
}
pub trait Pool {
type Data: 'static;
#[doc(hidden)]
fn ptr() -> &'static pool::Pool<ArcInner<Self::Data>>;
}
pub struct Arc<P>
where
P: Pool,
{
phantom: PhantomData<ArcInner<P::Data>>,
ptr: Ptr<Node<ArcInner<P::Data>>>,
pool: PhantomData<P>,
}
impl<P> Arc<P>
where
P: Pool,
{
pub fn new(data: P::Data) -> Result<Self, P::Data> {
if let Some(node) = P::ptr().stack.try_pop() {
unsafe {
ptr::write(
node.as_ref().data.get(),
ArcInner {
strong: AtomicUsize::new(1),
data,
},
)
}
Ok(Self {
phantom: PhantomData,
pool: PhantomData,
ptr: node,
})
} else {
Err(data)
}
}
fn inner(&self) -> &ArcInner<P::Data> {
unsafe { &*self.ptr.as_ref().data.get() }
}
fn from_inner(ptr: Ptr<Node<ArcInner<P::Data>>>) -> Self {
Self {
phantom: PhantomData,
pool: PhantomData,
ptr,
}
}
unsafe fn get_mut_unchecked(this: &mut Self) -> &mut P::Data {
&mut (*this.ptr.as_ref().data.get()).data
}
#[inline(never)]
unsafe fn drop_slow(&mut self) {
ptr::drop_in_place(Self::get_mut_unchecked(self));
P::ptr().stack.push(self.ptr);
}
}
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
impl<P> AsRef<P::Data> for Arc<P>
where
P: Pool,
{
fn as_ref(&self) -> &P::Data {
&**self
}
}
impl<P> Clone for Arc<P>
where
P: Pool,
{
fn clone(&self) -> Self {
let old_size = self.inner().strong.fetch_add(1, Ordering::Relaxed);
if old_size > MAX_REFCOUNT {
panic!();
}
Self::from_inner(self.ptr)
}
}
impl<P> fmt::Debug for Arc<P>
where
P: Pool,
P::Data: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<P> Deref for Arc<P>
where
P: Pool,
{
type Target = P::Data;
fn deref(&self) -> &P::Data {
&self.inner().data
}
}
impl<P> fmt::Display for Arc<P>
where
P: Pool,
P::Data: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<P> Drop for Arc<P>
where
P: Pool,
{
fn drop(&mut self) {
if self.inner().strong.fetch_sub(1, Ordering::Release) != 1 {
return;
}
atomic::fence(Ordering::Acquire);
unsafe {
self.drop_slow();
}
}
}
impl<P> Eq for Arc<P>
where
P: Pool,
P::Data: Eq,
{
}
impl<P> Hash for Arc<P>
where
P: Pool,
P::Data: Hash,
{
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
(**self).hash(state)
}
}
impl<P> Ord for Arc<P>
where
P: Pool,
P::Data: Ord,
{
fn cmp(&self, other: &Self) -> cmp::Ordering {
(**self).cmp(&**other)
}
}
impl<P> PartialEq for Arc<P>
where
P: Pool,
P::Data: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
(**self).eq(&**other)
}
}
impl<P> PartialOrd for Arc<P>
where
P: Pool,
P::Data: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
(**self).partial_cmp(&**other)
}
}
unsafe impl<P> Send for Arc<P>
where
P: Pool,
P::Data: Sync + Send,
{
}
unsafe impl<P> Sync for Arc<P>
where
P: Pool,
P::Data: Sync + Send,
{
}
impl<P> Unpin for Arc<P> where P: Pool {}
pub struct ArcInner<T> {
data: T,
strong: AtomicUsize,
}