Skip to main content

allocate/
sync.rs

1//! `Arc<T, A>` — atomically reference-counted handle backed by `A`.
2//!
3//! Non-intrusive: the refcount lives in a heap-allocated `ArcInner<T>`
4//! header (8 bytes), not on the payload `T`. Mirrors std `Arc`'s
5//! shape (`new_in`, `strong_count`, `get_mut`, `make_mut`, `as_ptr`,
6//! `ptr_eq`, `allocator`, `Deref<Target = T>`, `Clone`, `Drop`). No
7//! `Weak` — none of the workspace's current consumers want one.
8//!
9//! `allocator-api2` 0.2 does not ship an `Arc`, so we write our own
10//! against the api2 `Allocator` trait.
11
12use crate::{Allocator, Global};
13use core::marker::PhantomData;
14use core::ops::Deref;
15use core::ptr::NonNull;
16use core::sync::atomic::{AtomicUsize, Ordering, fence};
17
18struct ArcInner<T: ?Sized> {
19    strong: AtomicUsize,
20    data: T,
21}
22
23/// Atomically reference-counted handle to a `T` allocated by `A`.
24pub struct Arc<T: ?Sized, A: Allocator + Clone = Global> {
25    ptr: NonNull<ArcInner<T>>,
26    alloc: A,
27    _marker: PhantomData<ArcInner<T>>,
28}
29
30// Send/Sync mirror std `Arc<T, A>`: T must be Send+Sync because clones
31// of the Arc can move T to other threads (via the Arc) and share it.
32unsafe impl<T: ?Sized + Sync + Send, A: Allocator + Clone + Send> Send for Arc<T, A> {}
33unsafe impl<T: ?Sized + Sync + Send, A: Allocator + Clone + Sync> Sync for Arc<T, A> {}
34
35impl<T> Arc<T, Global> {
36    /// Allocate space for a `T` from `Global` and move `value` in.
37    #[inline]
38    pub fn new(value: T) -> Self {
39        Self::new_in(value, Global)
40    }
41}
42
43impl<T, A: Allocator + Clone> Arc<T, A> {
44    /// Allocate space for a `T` from `alloc` and move `value` in.
45    pub fn new_in(value: T, alloc: A) -> Self {
46        let layout = core::alloc::Layout::new::<ArcInner<T>>();
47        let raw = alloc
48            .allocate(layout)
49            .unwrap_or_else(|_| handle_alloc_error(layout));
50        let ptr = raw.cast::<ArcInner<T>>();
51        // SAFETY: `ptr` is a fresh allocation sized for `ArcInner<T>`
52        // and aligned to it.
53        unsafe {
54            core::ptr::write(
55                ptr.as_ptr(),
56                ArcInner {
57                    strong: AtomicUsize::new(1),
58                    data: value,
59                },
60            );
61        }
62        Self {
63            ptr,
64            alloc,
65            _marker: PhantomData,
66        }
67    }
68}
69
70impl<T: ?Sized, A: Allocator + Clone> Arc<T, A> {
71    #[inline]
72    fn inner(&self) -> &ArcInner<T> {
73        // SAFETY: `ptr` is always a valid initialised `ArcInner<T>`
74        // for the Arc's lifetime (allocated in `new_in`, freed in
75        // `Drop` when the last strong ref dies).
76        unsafe { self.ptr.as_ref() }
77    }
78
79    /// Current strong-reference count.
80    #[inline]
81    pub fn strong_count(this: &Self) -> usize {
82        this.inner().strong.load(Ordering::Acquire)
83    }
84
85    /// Mutable reference iff this is the unique strong owner.
86    #[inline]
87    pub fn get_mut(this: &mut Self) -> Option<&mut T> {
88        if Self::strong_count(this) == 1 {
89            // SAFETY: strong == 1 and we hold `&mut Self`, so no other
90            // Arc<T, A> can be aliasing the inner data.
91            Some(unsafe { &mut (*this.ptr.as_ptr()).data })
92        } else {
93            None
94        }
95    }
96
97    /// Raw pointer to the inner `T`. The Arc remains the owner.
98    #[inline]
99    pub fn as_ptr(this: &Self) -> *const T {
100        // SAFETY: inner is always live for the Arc's lifetime.
101        unsafe { core::ptr::addr_of!((*this.ptr.as_ptr()).data) }
102    }
103
104    /// Pointer-equality test.
105    #[inline]
106    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
107        core::ptr::addr_eq(this.ptr.as_ptr(), other.ptr.as_ptr())
108    }
109
110    /// Borrow the underlying allocator.
111    #[inline]
112    pub fn allocator(this: &Self) -> &A {
113        &this.alloc
114    }
115}
116
117impl<T: Clone, A: Allocator + Clone> Arc<T, A> {
118    /// Return `&mut T`. If this is the sole strong owner, mutate in
119    /// place; otherwise deep-clone the `T` into a fresh `Arc` slab,
120    /// replace `*this` with the fresh handle, and let the original
121    /// `Arc` drop.
122    ///
123    /// Panics on allocation failure in the clone path.
124    pub fn make_mut(this: &mut Self) -> &mut T {
125        if Self::strong_count(this) != 1 {
126            let cloned = this.inner().data.clone();
127            *this = Self::new_in(cloned, this.alloc.clone());
128        }
129        // SAFETY: strong == 1 (either because it already was, or
130        // because we just replaced `*this` with a fresh Arc whose
131        // strong starts at 1) and we hold `&mut Self`.
132        unsafe { &mut (*this.ptr.as_ptr()).data }
133    }
134}
135
136impl<T: ?Sized, A: Allocator + Clone> Deref for Arc<T, A> {
137    type Target = T;
138    #[inline]
139    fn deref(&self) -> &T {
140        &self.inner().data
141    }
142}
143
144impl<T: ?Sized, A: Allocator + Clone> Clone for Arc<T, A> {
145    fn clone(&self) -> Self {
146        // Relaxed is fine: the Arc we're cloning already synchronises
147        // with anyone who might observe the new strong count, and the
148        // bumped count can't underflow because *this Arc keeps it ≥ 1.
149        let old = self.inner().strong.fetch_add(1, Ordering::Relaxed);
150        // Saturation guard: refcount overflow is a hard error. std Arc
151        // aborts; we panic (no_std-friendly via core::panic).
152        if old > isize::MAX as usize {
153            arc_overflow();
154        }
155        Self {
156            ptr: self.ptr,
157            alloc: self.alloc.clone(),
158            _marker: PhantomData,
159        }
160    }
161}
162
163impl<T: ?Sized, A: Allocator + Clone> Drop for Arc<T, A> {
164    fn drop(&mut self) {
165        // Release the strong ref. If we weren't last, nothing more to
166        // do; another thread will run the destructor.
167        if self.inner().strong.fetch_sub(1, Ordering::Release) != 1 {
168            return;
169        }
170        // Synchronise with all previous Releases so we observe the
171        // final state of the payload before dropping it.
172        fence(Ordering::Acquire);
173
174        let layout = core::alloc::Layout::for_value(self.inner());
175        // SAFETY: we are the last strong reference; the payload is
176        // valid and uniquely owned by us, so dropping it in place is
177        // sound. The allocation came from `self.alloc` (via `new_in`)
178        // with this exact layout.
179        unsafe {
180            core::ptr::drop_in_place(&raw mut (*self.ptr.as_ptr()).data);
181            self.alloc.deallocate(self.ptr.cast::<u8>(), layout);
182        }
183    }
184}
185
186impl<T: ?Sized + core::fmt::Debug, A: Allocator + Clone> core::fmt::Debug for Arc<T, A> {
187    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
188        core::fmt::Debug::fmt(&self.inner().data, f)
189    }
190}
191
192#[cold]
193#[inline(never)]
194fn handle_alloc_error(_layout: core::alloc::Layout) -> ! {
195    // Mirror std behaviour: allocation failure inside Arc::new_in is
196    // unrecoverable. Callers who care about fallibility should use
197    // try_*_in on Box / Vec directly; Arc has no try-form in std and
198    // we follow suit.
199    panic!("Arc: allocation failed");
200}
201
202#[cold]
203#[inline(never)]
204fn arc_overflow() -> ! {
205    panic!("Arc: strong refcount overflowed isize::MAX");
206}