Skip to main content

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}