triomphe/arc_union.rs
1use core::fmt;
2use core::marker::PhantomData;
3use core::panic::{RefUnwindSafe, UnwindSafe};
4use core::ptr;
5
6use super::{Arc, ArcBorrow};
7
8/// A tagged union that can represent `Arc<A>` or `Arc<B>` while only consuming a
9/// single word. The type is also `NonNull`, and thus can be stored in an Option
10/// without increasing size.
11///
12/// This is functionally equivalent to
13/// `enum ArcUnion<A, B> { First(Arc<A>), Second(Arc<B>)` but only takes up
14/// up a single word of stack space.
15///
16/// This could probably be extended to support four types if necessary.
17pub struct ArcUnion<A, B> {
18 // Safety invariant: `p` is a non-null pointer derived from `Arc::into_raw`
19 // that owns one strong reference to a live `ArcInner<A>` (when `p.addr() & 1 == 0`)
20 // or `ArcInner<B>` (when `p.addr() & 1 == 1`, with bit 0 set via `map_addr`).
21 // Because `ArcInner<T>` is `#[repr(C)]` starting with `AtomicUsize` (whose
22 // size and alignment are `align_of::<usize>() >= 2`), the `data` field
23 // pointer returned by `Arc::into_raw` is always at least 2-byte aligned
24 // (bit 0 is 0), and `p` retains full provenance over the `ArcInner` allocation.
25 p: ptr::NonNull<()>,
26 phantom_a: PhantomData<A>,
27 phantom_b: PhantomData<B>,
28}
29
30// SAFETY:
31// `ArcUnion<A, B>` logically owns either an `Arc<A>` or an `Arc<B>`. Moving it
32// across threads requires both `Arc<A>: Send` (`A: Sync + Send`) and
33// `Arc<B>: Send` (`B: Sync + Send`).
34unsafe impl<A: Sync + Send, B: Send + Sync> Send for ArcUnion<A, B> {}
35
36// SAFETY:
37// Sharing `&ArcUnion<A, B>` across threads permits borrowing `&A` or `&B` and
38// cloning the underlying `Arc`, which is sound when `A: Sync + Send` and
39// `B: Sync + Send`.
40unsafe impl<A: Sync + Send, B: Send + Sync> Sync for ArcUnion<A, B> {}
41
42impl<A: RefUnwindSafe, B: RefUnwindSafe> UnwindSafe for ArcUnion<A, B> {}
43
44impl<A: PartialEq, B: PartialEq> PartialEq for ArcUnion<A, B> {
45 fn eq(&self, other: &Self) -> bool {
46 use crate::ArcUnionBorrow::*;
47 match (self.borrow(), other.borrow()) {
48 (First(x), First(y)) => x == y,
49 (Second(x), Second(y)) => x == y,
50 (_, _) => false,
51 }
52 }
53}
54
55/// This represents a borrow of an `ArcUnion`.
56#[derive(Debug)]
57pub enum ArcUnionBorrow<'a, A: 'a, B: 'a> {
58 First(ArcBorrow<'a, A>),
59 Second(ArcBorrow<'a, B>),
60}
61
62impl<A, B> ArcUnion<A, B> {
63 /// # Safety
64 ///
65 /// The caller must ensure that `ptr` is non-null, retains full provenance
66 /// over a live `ArcInner<A>` or `ArcInner<B>` allocation from `Arc::into_raw`,
67 /// transfers ownership of one strong reference to the returned `ArcUnion`,
68 /// and has bit `0x1` clear if it points to `A` or set (via `map_addr`) if
69 /// it points to `B`.
70 const unsafe fn new(ptr: *mut ()) -> Self {
71 ArcUnion {
72 // SAFETY: By this function's `# Safety` precondition, `ptr` is non-null.
73 p: unsafe { ptr::NonNull::new_unchecked(ptr) },
74 phantom_a: PhantomData,
75 phantom_b: PhantomData,
76 }
77 }
78
79 /// Returns true if the two values are pointer-equal.
80 #[inline]
81 pub fn ptr_eq(this: &Self, other: &Self) -> bool {
82 this.p == other.p
83 }
84
85 /// Reference count.
86 #[inline]
87 pub fn strong_count(this: &Self) -> usize {
88 ArcUnionBorrow::strong_count(&this.borrow())
89 }
90
91 /// Returns an enum representing a borrow of either A or B.
92 pub fn borrow(&self) -> ArcUnionBorrow<'_, A, B> {
93 if self.is_first() {
94 let ptr = self.p.as_ptr() as *const A;
95 // SAFETY:
96 // `self.is_first()` checked that bit `0x1` of `self.p` is 0. By
97 // `ArcUnion::p`'s field invariant, `ptr` is the untagged pointer to
98 // `A` obtained from `Arc::into_raw` with full provenance over the
99 // live `ArcInner<A>` owned by `self` for the lifetime of `&self`.
100 let borrow = unsafe { ArcBorrow::from_ptr(ptr) };
101 ArcUnionBorrow::First(borrow)
102 } else {
103 let ptr = self.p.as_ptr().map_addr(|addr| addr & !0x1) as *const B;
104 // SAFETY:
105 // `!self.is_first()` means bit `0x1` of `self.p` is 1. By
106 // `ArcUnion::p`'s field invariant, clearing bit `0x1` via `map_addr`
107 // recovers the original 2-byte-aligned pointer to `B` from
108 // `Arc::into_raw` while preserving full provenance over the live
109 // `ArcInner<B>` owned by `self` for the lifetime of `&self`.
110 let borrow = unsafe { ArcBorrow::from_ptr(ptr) };
111 ArcUnionBorrow::Second(borrow)
112 }
113 }
114
115 /// Creates an `ArcUnion` from an instance of the first type.
116 #[inline]
117 pub const fn from_first(other: Arc<A>) -> Self {
118 // SAFETY:
119 // `Arc::into_raw(other)` returns a non-null pointer to `ArcInner<A>::data`
120 // with full provenance over `ArcInner<A>`, transferring ownership of one
121 // strong reference. Because `ArcInner<A>` is `#[repr(C)]` with a leading
122 // `AtomicUsize` (alignment >= 2), the offset and address of `data` are
123 // even, so bit `0x1` is 0 as required for the `First` variant.
124 unsafe { Self::new(Arc::into_raw(other) as *mut _) }
125 }
126
127 /// Creates an `ArcUnion` from an instance of the second type.
128 #[inline]
129 pub fn from_second(other: Arc<B>) -> Self {
130 let ptr = Arc::into_raw(other);
131 let tagged = ptr.map_addr(|addr| addr | 0x1);
132 // SAFETY:
133 // `ptr` came from `Arc::into_raw(other)` and is non-null with full
134 // provenance over `ArcInner<B>` and an even address (bit `0x1` clear).
135 // Setting bit `0x1` via `map_addr` preserves provenance and produces a
136 // non-null pointer with bit `0x1` set, transferring ownership of one
137 // strong reference for the `Second` variant.
138 unsafe { Self::new(tagged as *mut _) }
139 }
140
141 /// Returns true if this `ArcUnion` contains the first type.
142 #[inline]
143 pub fn is_first(&self) -> bool {
144 self.p.as_ptr() as usize & 0x1 == 0
145 }
146
147 /// Returns true if this `ArcUnion` contains the second type.
148 #[inline]
149 pub fn is_second(&self) -> bool {
150 !self.is_first()
151 }
152
153 /// Returns a borrow of the first type if applicable, otherwise `None`.
154 pub fn as_first(&self) -> Option<ArcBorrow<'_, A>> {
155 match self.borrow() {
156 ArcUnionBorrow::First(x) => Some(x),
157 ArcUnionBorrow::Second(_) => None,
158 }
159 }
160
161 /// Returns a borrow of the second type if applicable, otherwise None.
162 pub fn as_second(&self) -> Option<ArcBorrow<'_, B>> {
163 match self.borrow() {
164 ArcUnionBorrow::First(_) => None,
165 ArcUnionBorrow::Second(x) => Some(x),
166 }
167 }
168}
169
170impl<A, B> Clone for ArcUnion<A, B> {
171 fn clone(&self) -> Self {
172 match self.borrow() {
173 ArcUnionBorrow::First(x) => ArcUnion::from_first(x.clone_arc()),
174 ArcUnionBorrow::Second(x) => ArcUnion::from_second(x.clone_arc()),
175 }
176 }
177}
178
179impl<A, B> Drop for ArcUnion<A, B> {
180 fn drop(&mut self) {
181 match self.borrow() {
182 // SAFETY:
183 // `x` was constructed in `self.borrow()` from the untagged raw
184 // pointer originally produced by `Arc::into_raw`. `ArcBorrow::to_raw(&x)`
185 // extracts that pointer without creating an intermediate `&A`
186 // reference, preserving provenance over the `ArcInner<A>` header.
187 // Reconstructing and dropping `Arc<A>` consumes the single strong
188 // reference owned by `*self`, which is being dropped and will not
189 // be accessed again.
190 ArcUnionBorrow::First(x) => unsafe {
191 let _ = Arc::from_raw(ArcBorrow::to_raw(&x));
192 },
193 // SAFETY:
194 // Same as the `First` arm for `ArcInner<B>`: `ArcBorrow::to_raw(&x)`
195 // returns the untagged pointer with full `ArcInner<B>` provenance,
196 // and dropping the reconstructed `Arc<B>` consumes the strong
197 // reference owned by `*self`.
198 ArcUnionBorrow::Second(x) => unsafe {
199 let _ = Arc::from_raw(ArcBorrow::to_raw(&x));
200 },
201 }
202 }
203}
204
205impl<A: fmt::Debug, B: fmt::Debug> fmt::Debug for ArcUnion<A, B> {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 fmt::Debug::fmt(&self.borrow(), f)
208 }
209}
210
211impl<'a, A, B> ArcUnionBorrow<'a, A, B> {
212 /// The reference count of this `Arc`.
213 ///
214 /// The number does not include borrowed pointers,
215 /// or temporary `Arc` pointers created with functions like
216 /// [`ArcBorrow::with_arc`].
217 ///
218 /// The function is called `strong_count` to mirror `std::sync::Arc::strong_count`,
219 /// however `triomphe::Arc` does not support weak references.
220 #[inline]
221 pub fn strong_count(this: &Self) -> usize {
222 match this {
223 ArcUnionBorrow::First(arc) => ArcBorrow::strong_count(arc),
224 ArcUnionBorrow::Second(arc) => ArcBorrow::strong_count(arc),
225 }
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[test]
234 fn safe_arc_union_drop_reaches_refcount_through_data_pointer() {
235 let union = ArcUnion::<u8, u64>::from_first(Arc::new(0xA5));
236 drop(union);
237 }
238
239 #[test]
240 fn arc_union_first_and_second_smoke() {
241 let a = Arc::new(123_u32);
242 let b = Arc::new(456_u64);
243
244 let u_a = ArcUnion::<u32, u64>::from_first(a.clone());
245 let u_b = ArcUnion::<u32, u64>::from_second(b.clone());
246
247 assert!(u_a.is_first());
248 assert!(!u_a.is_second());
249 assert!(!u_b.is_first());
250 assert!(u_b.is_second());
251
252 assert_eq!(ArcUnion::strong_count(&u_a), 2);
253 assert_eq!(ArcUnion::strong_count(&u_b), 2);
254
255 assert_eq!(*u_a.as_first().unwrap(), 123);
256 assert!(u_a.as_second().is_none());
257 assert!(u_b.as_first().is_none());
258 assert_eq!(*u_b.as_second().unwrap(), 456);
259
260 let u_a2 = u_a.clone();
261 assert_eq!(ArcUnion::strong_count(&u_a), 3);
262 assert_eq!(u_a, u_a2);
263 assert_ne!(u_a, u_b);
264
265 drop(u_a2);
266 assert_eq!(ArcUnion::strong_count(&u_a), 2);
267 drop(u_a);
268 assert_eq!(Arc::strong_count(&a), 1);
269
270 drop(u_b);
271 assert_eq!(Arc::strong_count(&b), 1);
272 }
273
274 #[test]
275 fn arc_union_zst() {
276 let a = Arc::new(());
277 let b = Arc::new(());
278
279 let u_a = ArcUnion::<(), ()>::from_first(a.clone());
280 let u_b = ArcUnion::<(), ()>::from_second(b.clone());
281
282 assert!(u_a.is_first());
283 assert!(u_b.is_second());
284
285 assert_eq!(ArcUnion::strong_count(&u_a), 2);
286 assert_eq!(ArcUnion::strong_count(&u_b), 2);
287
288 drop(u_a);
289 drop(u_b);
290
291 assert_eq!(Arc::strong_count(&a), 1);
292 assert_eq!(Arc::strong_count(&b), 1);
293 }
294}