triomphe/
offset_arc.rs

1use core::fmt;
2use core::marker::PhantomData;
3use core::mem::ManuallyDrop;
4use core::ops::Deref;
5use core::panic::{RefUnwindSafe, UnwindSafe};
6use core::ptr;
7
8use super::{Arc, ArcBorrow};
9
10/// An `Arc`, except it holds a pointer to the T instead of to the
11/// entire ArcInner.
12///
13/// An `OffsetArc<T>` has the same layout and ABI as a non-null
14/// `const T*` in C, and may be used in FFI function signatures.
15///
16/// ```text
17///  Arc<T>    OffsetArc<T>
18///   |          |
19///   v          v
20///  ---------------------
21/// | RefCount | T (data) | [ArcInner<T>]
22///  ---------------------
23/// ```
24///
25/// This means that this is a direct pointer to
26/// its contained data (and can be read from by both C++ and Rust),
27/// but we can also convert it to a "regular" `Arc<T>` by removing the offset.
28///
29/// This is very useful if you have an Arc-containing struct shared between Rust and C++,
30/// and wish for C++ to be able to read the data behind the `Arc` without incurring
31/// an FFI call overhead.
32#[derive(Eq)]
33#[repr(transparent)]
34pub struct OffsetArc<T> {
35    pub(crate) ptr: ptr::NonNull<T>,
36    pub(crate) phantom: PhantomData<T>,
37}
38
39unsafe impl<T: Sync + Send> Send for OffsetArc<T> {}
40unsafe impl<T: Sync + Send> Sync for OffsetArc<T> {}
41
42impl<T: RefUnwindSafe> UnwindSafe for OffsetArc<T> {}
43
44impl<T> Deref for OffsetArc<T> {
45    type Target = T;
46    #[inline]
47    fn deref(&self) -> &Self::Target {
48        unsafe { &*self.ptr.as_ptr() }
49    }
50}
51
52impl<T> Clone for OffsetArc<T> {
53    #[inline]
54    fn clone(&self) -> Self {
55        Arc::into_raw_offset(self.clone_arc())
56    }
57}
58
59impl<T> Drop for OffsetArc<T> {
60    fn drop(&mut self) {
61        let _ = Arc::from_raw_offset(OffsetArc {
62            ptr: self.ptr,
63            phantom: PhantomData,
64        });
65    }
66}
67
68impl<T: fmt::Debug> fmt::Debug for OffsetArc<T> {
69    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70        fmt::Debug::fmt(&**self, f)
71    }
72}
73
74impl<T: PartialEq> PartialEq for OffsetArc<T> {
75    fn eq(&self, other: &OffsetArc<T>) -> bool {
76        *(*self) == *(*other)
77    }
78
79    #[allow(clippy::partialeq_ne_impl)]
80    fn ne(&self, other: &OffsetArc<T>) -> bool {
81        *(*self) != *(*other)
82    }
83}
84
85impl<T> OffsetArc<T> {
86    /// Temporarily converts |self| into a bonafide Arc and exposes it to the
87    /// provided callback. The refcount is not modified.
88    #[inline]
89    pub fn with_arc<F, U>(&self, f: F) -> U
90    where
91        F: FnOnce(&Arc<T>) -> U,
92    {
93        // Synthesize transient Arc, which never touches the refcount of the ArcInner.
94        let transient = unsafe { ManuallyDrop::new(Arc::from_raw(self.ptr.as_ptr())) };
95
96        // Expose the transient Arc to the callback, which may clone it if it wants
97        // and forward the result to the user
98        f(&transient)
99    }
100
101    /// If uniquely owned, provide a mutable reference
102    /// Else create a copy, and mutate that
103    ///
104    /// This is functionally the same thing as `Arc::make_mut`
105    #[inline]
106    pub fn make_mut(&mut self) -> &mut T
107    where
108        T: Clone,
109    {
110        unsafe {
111            // extract the OffsetArc as an owned variable. This does not modify
112            // the refcount and we should be careful to not drop `this`
113            let this = ptr::read(self);
114            // treat it as a real Arc, but wrapped in a ManuallyDrop
115            // in case `Arc::make_mut()` panics in the clone impl
116            let mut arc = ManuallyDrop::new(Arc::from_raw_offset(this));
117            // obtain the mutable reference. Cast away the lifetime since
118            // we have the right lifetime bounds in the parameters.
119            // This may mutate `arc`.
120            let ret = Arc::make_mut(&mut *arc) as *mut _;
121            // Store the possibly-mutated arc back inside, after converting
122            // it to a OffsetArc again. Release the ManuallyDrop.
123            // This also does not modify the refcount or call drop on self
124            ptr::write(self, Arc::into_raw_offset(ManuallyDrop::into_inner(arc)));
125            &mut *ret
126        }
127    }
128
129    /// Clone it as an `Arc`
130    #[inline]
131    pub fn clone_arc(&self) -> Arc<T> {
132        OffsetArc::with_arc(self, |a| a.clone())
133    }
134
135    /// Produce a pointer to the data that can be converted back
136    /// to an `Arc`
137    #[inline]
138    pub fn borrow_arc(&self) -> ArcBorrow<'_, T> {
139        ArcBorrow(self.ptr, PhantomData)
140    }
141
142    /// The reference count of this `Arc`.
143    ///
144    /// The number does not include borrowed pointers,
145    /// or temporary `Arc` pointers created with functions like
146    /// [`ArcBorrow::with_arc`].
147    ///
148    /// The function is called `strong_count` to mirror `std::sync::Arc::strong_count`,
149    /// however `triomphe::Arc` does not support weak references.
150    #[inline]
151    pub fn strong_count(this: &Self) -> usize {
152        Self::with_arc(this, |arc| Arc::strong_count(arc))
153    }
154}