#![allow(
clippy::type_repetition_in_bounds,
reason = "trait-impl `where` clauses are kept uniform across all forwarding impls"
)]
use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::pin::Pin;
use core::ptr::{self, NonNull};
use core::sync::atomic::{Ordering, fence};
use allocator_api2::alloc::{Allocator, Global};
use ptr_meta::Pointee;
use crate::internal::chunk_ref::ChunkRef;
use crate::internal::constants::refcount_overflow_abort;
use crate::internal::thin_dst;
use crate::thin_smart_ptr_common::impl_thin_smart_ptr_common;
use crate::vec::Vec;
const MAX_STRONG_REFCOUNT: u32 = u32::MAX >> 1;
pub struct Arc<T: ?Sized + Pointee, A: Allocator + Clone = Global> {
ptr: NonNull<u8>,
_phantom: PhantomData<(*const T, A)>,
}
impl<T: ?Sized + Pointee, A: Allocator + Clone> Arc<T, A> {
#[inline]
pub(crate) unsafe fn from_raw(thin: NonNull<u8>) -> Self {
Self {
ptr: thin,
_phantom: PhantomData,
}
}
#[inline]
pub(crate) fn thin_ptr(&self) -> NonNull<u8> {
self.ptr
}
#[inline]
#[must_use]
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
ptr::addr_eq(a.ptr.as_ptr(), b.ptr.as_ptr())
}
}
impl_thin_smart_ptr_common!(Arc);
impl<T, A: Allocator + Clone> Arc<MaybeUninit<T>, A> {
#[inline]
#[must_use]
pub unsafe fn assume_init(self) -> Arc<T, A> {
let thin = self.ptr;
mem::forget(self);
unsafe { Arc::from_raw(thin) }
}
#[must_use]
#[inline]
pub unsafe fn assume_init_pin(this: Pin<Self>) -> Pin<Arc<T, A>>
where
A: 'static,
{
unsafe {
let inner: Self = Pin::into_inner_unchecked(this);
Arc::into_pin(inner.assume_init())
}
}
}
impl<T, A: Allocator + Clone> Arc<[MaybeUninit<T>], A> {
#[inline]
#[must_use]
pub unsafe fn assume_init(self) -> Arc<[T], A> {
let thin = self.ptr;
mem::forget(self);
unsafe { Arc::from_raw(thin) }
}
#[must_use]
#[inline]
pub unsafe fn assume_init_pin_slice(this: Pin<Self>) -> Pin<Arc<[T], A>>
where
A: 'static,
{
unsafe {
let inner: Self = Pin::into_inner_unchecked(this);
Arc::into_pin(inner.assume_init())
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[inline(never)]
#[cold]
fn strong_overflow_abort() -> ! {
refcount_overflow_abort()
}
impl<T: ?Sized + Pointee, A: Allocator + Clone> Clone for Arc<T, A> {
#[inline]
fn clone(&self) -> Self {
let value_align = mem::align_of_val::<T>(&**self);
let strong = unsafe { thin_dst::strong_ref::<T>(self.ptr, value_align) };
let prev = strong.fetch_add(1, Ordering::Relaxed);
if prev > MAX_STRONG_REFCOUNT {
strong_overflow_abort();
}
Self {
ptr: self.ptr,
_phantom: PhantomData,
}
}
}
impl<T: ?Sized + Pointee, A: Allocator + Clone> Drop for Arc<T, A> {
#[inline]
fn drop(&mut self) {
let value_align = mem::align_of_val::<T>(&**self);
let strong = unsafe { thin_dst::strong_ref::<T>(self.ptr, value_align) };
if strong.fetch_sub(1, Ordering::Release) != 1 {
return;
}
fence(Ordering::Acquire);
unsafe {
let _chunk: ChunkRef<A> = ChunkRef::from_value_ptr(self.ptr);
let fat = self.as_fat_ptr();
ptr::drop_in_place(fat.as_ptr());
}
}
}
unsafe impl<T: ?Sized + Pointee + Sync + Send, A: Allocator + Clone + Send + Sync> Send for Arc<T, A> {}
unsafe impl<T: ?Sized + Pointee + Sync + Send, A: Allocator + Clone + Send + Sync> Sync for Arc<T, A> {}
impl<'a, T, A: Allocator + Clone> From<Vec<'a, T, A>> for Arc<[T], A>
where
T: Send + Sync,
A: Send + Sync,
{
#[inline]
fn from(v: Vec<'a, T, A>) -> Self {
v.into_arc_slice()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Arena;
#[test]
fn max_strong_refcount_is_u32_half_range() {
assert_eq!(MAX_STRONG_REFCOUNT, u32::MAX >> 1);
assert_eq!(MAX_STRONG_REFCOUNT, 0x7FFF_FFFF);
}
#[test]
fn clone_at_max_refcount_threshold_does_not_abort() {
let arena = Arena::new();
let arc = arena.alloc_arc(0xABCD_u32);
let strong = unsafe { thin_dst::strong_ref::<u32>(arc.thin_ptr(), mem::align_of::<u32>()) };
strong.store(MAX_STRONG_REFCOUNT, Ordering::Relaxed);
#[expect(
clippy::redundant_clone,
reason = "exercising Arc::clone's overflow guard at the threshold is the point of the test"
)]
let clone = arc.clone();
assert_eq!(*clone, 0xABCD);
strong.store(2, Ordering::Relaxed);
}
#[test]
#[should_panic(expected = "refcount overflow")]
fn clone_above_max_refcount_threshold_aborts() {
let arena = Arena::new();
let arc = arena.alloc_arc(0xABCD_u32);
let strong = unsafe { thin_dst::strong_ref::<u32>(arc.thin_ptr(), mem::align_of::<u32>()) };
strong.store(MAX_STRONG_REFCOUNT + 1, Ordering::Relaxed);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _c = arc.clone();
}));
strong.store(1, Ordering::Relaxed);
std::panic::resume_unwind(result.expect_err("clone past the threshold must panic"));
}
}