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}