Skip to main content

sdd/
shared.rs

1use std::mem::forget;
2use std::ops::Deref;
3use std::panic::UnwindSafe;
4use std::ptr::NonNull;
5
6use super::ref_counted::RefCounted;
7use super::{Guard, Ptr};
8
9/// [`Shared`] is a reference-counted handle to an instance.
10///
11/// The instance is passed to the EBR garbage collector when the last strong reference is dropped.
12#[derive(Debug)]
13pub struct Shared<T> {
14    ptr: NonNull<RefCounted<T>>,
15}
16
17impl<T: 'static> Shared<T> {
18    /// Creates a new [`Shared`].
19    ///
20    /// The type of the instance must be determined at compile-time, must not contain non-static
21    /// references, and must not be a non-static reference since the instance can theoretically
22    /// survive the process. For instance, `struct Disallowed<'l, T>(&'l T)` is not allowed,
23    /// because an instance of the type cannot outlive `'l` whereas the garbage collector does not
24    /// guarantee that the instance is dropped within `'l`.
25    ///
26    /// # Examples
27    ///
28    /// ```
29    /// use sdd::Shared;
30    ///
31    /// let shared: Shared<usize> = Shared::new(31);
32    /// ```
33    #[inline]
34    pub fn new(t: T) -> Self {
35        Self::new_with(|| t)
36    }
37
38    /// Creates a new [`Shared`] with the provided function.
39    ///
40    /// This function is identical to [`Shared::new`] except that the value is constructed after
41    /// memory allocation.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use sdd::Shared;
47    ///
48    /// let shared: Shared<String> = Shared::new_with(|| String::from("hello"));
49    /// ```
50    #[inline]
51    pub fn new_with<F: FnOnce() -> T>(f: F) -> Shared<T> {
52        Shared {
53            ptr: RefCounted::new_shared(f),
54        }
55    }
56}
57
58impl<T> Shared<T> {
59    /// Creates a new [`Shared`] without checking the lifetime of `T`.
60    ///
61    /// # Safety
62    ///
63    /// `T::drop` can be run after the last strong reference is dropped, therefore it is safe only
64    /// if `T::drop` does not access short-lived data or [`std::mem::needs_drop`] is `false` for
65    /// `T`. Otherwise, the instance must be manually dropped by invoking [`Self::drop_in_place`]
66    /// within the lifetime of `T`.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use sdd::Shared;
72    ///
73    /// let hello = String::from("hello");
74    /// let shared: Shared<&str> = unsafe { Shared::new_unchecked(hello.as_str()) };
75    ///
76    /// assert!(unsafe { shared.drop_in_place() });
77    /// ```
78    #[inline]
79    pub unsafe fn new_unchecked(t: T) -> Self {
80        unsafe { Self::new_with_unchecked(|| t) }
81    }
82
83    /// Creates a new [`Shared`] with the provided function without checking the lifetime of `T`.
84    ///
85    /// This function is identical to [`Shared::new_unchecked`] except that the value is constructed
86    /// after memory allocation.
87    ///
88    /// # Safety
89    ///
90    /// See [`Shared::new_unchecked`].
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use sdd::Shared;
96    ///
97    /// let hello = String::from("hello");
98    /// let shared: Shared<&str> = unsafe { Shared::new_with_unchecked(|| hello.as_str()) };
99    ///
100    /// assert!(unsafe { shared.drop_in_place() });
101    /// ```
102    #[inline]
103    pub unsafe fn new_with_unchecked<F: FnOnce() -> T>(f: F) -> Shared<T> {
104        Shared {
105            ptr: RefCounted::new_shared(f),
106        }
107    }
108
109    /// Returns a [`Ptr`] to the instance that may live as long as the supplied [`Guard`].
110    ///
111    /// # Examples
112    ///
113    /// ```
114    /// use sdd::{Guard, Shared};
115    ///
116    /// let shared: Shared<usize> = Shared::new(37);
117    /// let guard = Guard::new();
118    /// let ptr = shared.get_guarded_ptr(&guard);
119    /// drop(shared);
120    ///
121    /// assert_eq!(*ptr.as_ref().unwrap(), 37);
122    /// ```
123    #[inline]
124    #[must_use]
125    pub const fn get_guarded_ptr<'g>(&self, _guard: &'g Guard) -> Ptr<'g, T> {
126        Ptr::from(self.ptr.as_ptr())
127    }
128
129    /// Returns a reference to the instance that may live as long as the supplied [`Guard`].
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// use sdd::{Guard, Shared};
135    ///
136    /// let shared: Shared<usize> = Shared::new(37);
137    /// let guard = Guard::new();
138    /// let ref_b = shared.get_guarded_ref(&guard);
139    /// drop(shared);
140    ///
141    /// assert_eq!(*ref_b, 37);
142    /// ```
143    #[inline]
144    #[must_use]
145    pub const fn get_guarded_ref<'g>(&self, _guard: &'g Guard) -> &'g T {
146        unsafe { RefCounted::inst_non_null_ptr(self.ptr).as_ref() }
147    }
148
149    /// Returns a mutable reference to the instance if the [`Shared`] is holding the only strong
150    /// reference.
151    ///
152    /// # Safety
153    ///
154    /// The method is `unsafe` since there can be a [`Ptr`] to the instance without holding a
155    /// strong reference.
156    ///
157    /// # Examples
158    ///
159    /// ```
160    /// use sdd::Shared;
161    ///
162    /// let mut shared: Shared<usize> = Shared::new(38);
163    /// unsafe {
164    ///     *shared.get_mut().unwrap() += 1;
165    /// }
166    /// assert_eq!(*shared, 39);
167    /// ```
168    #[inline]
169    pub unsafe fn get_mut(&mut self) -> Option<&mut T> {
170        unsafe { self.ptr.as_ptr().as_mut().and_then(|r| r.get_mut_shared()) }
171    }
172
173    /// Provides a raw pointer to the instance.
174    ///
175    /// # Examples
176    ///
177    /// ```
178    /// use sdd::Shared;
179    ///
180    /// let shared: Shared<usize> = Shared::new(10);
181    /// let shared_clone: Shared<usize> = shared.clone();
182    ///
183    /// assert_eq!(shared.as_ptr(), shared_clone.as_ptr());
184    /// assert_eq!(unsafe { *shared.as_ptr() }, unsafe { *shared_clone.as_ptr() });
185    /// ```
186    #[inline]
187    #[must_use]
188    pub const fn as_ptr(&self) -> *const T {
189        RefCounted::inst_non_null_ptr(self.ptr).as_ptr()
190    }
191
192    /// Provides a raw non-null pointer to the instance.
193    ///
194    /// # Examples
195    ///
196    /// ```
197    /// use sdd::Shared;
198    ///
199    /// let shared: Shared<usize> = Shared::new(10);
200    /// let shared_clone: Shared<usize> = shared.clone();
201    ///
202    /// assert_eq!(shared.as_ptr(), shared_clone.as_ptr());
203    /// assert_eq!(unsafe { *shared.as_non_null_ptr().as_ref() }, unsafe { *shared_clone.as_ptr() });
204    /// ```
205    #[inline]
206    #[must_use]
207    pub const fn as_non_null_ptr(&self) -> NonNull<T> {
208        RefCounted::inst_non_null_ptr(self.ptr)
209    }
210
211    /// Releases the strong reference by passing `self` to the given [`Guard`].
212    ///
213    /// Returns `true` if the last reference was released.
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use sdd::Shared;
219    ///
220    /// let shared: Shared<usize> = Shared::new(47);
221    /// let shared_clone = shared.clone();
222    /// assert!(!shared.release());
223    /// assert!(shared_clone.release());
224    /// ```
225    #[inline]
226    #[must_use]
227    pub fn release(self) -> bool {
228        let released = if unsafe { (*self.ptr.as_ptr()).drop_ref() } {
229            RefCounted::pass_to_collector(self.ptr.as_ptr());
230            true
231        } else {
232            false
233        };
234        forget(self);
235        released
236    }
237
238    /// Drops the instance immediately if it has held the last reference to the instance.
239    ///
240    /// Returns `true` if the instance was dropped.
241    ///
242    /// # Safety
243    ///
244    /// The caller must ensure that there is no [`Ptr`] pointing to the instance.
245    ///
246    /// # Examples
247    ///
248    /// ```
249    /// use sdd::Shared;
250    /// use std::sync::atomic::AtomicBool;
251    /// use std::sync::atomic::Ordering::Relaxed;
252    ///
253    /// static DROPPED: AtomicBool = AtomicBool::new(false);
254    /// struct T(&'static AtomicBool);
255    /// impl Drop for T {
256    ///     fn drop(&mut self) {
257    ///         self.0.store(true, Relaxed);
258    ///     }
259    /// }
260    ///
261    /// let shared: Shared<T> = Shared::new(T(&DROPPED));
262    /// let shared_clone = shared.clone();
263    ///
264    /// unsafe {
265    ///     assert!(!shared.drop_in_place());
266    ///     assert!(!DROPPED.load(Relaxed));
267    ///     assert!(shared_clone.drop_in_place());
268    ///     assert!(DROPPED.load(Relaxed));
269    /// }
270    /// ```
271    #[inline]
272    #[must_use]
273    pub unsafe fn drop_in_place(self) -> bool {
274        unsafe {
275            let dropped = if (*self.ptr.as_ptr()).drop_ref() {
276                RefCounted::<T>::dealloc(self.ptr.as_ptr());
277                true
278            } else {
279                false
280            };
281            forget(self);
282            dropped
283        }
284    }
285
286    /// Creates a new [`Shared`] from the given pointer.
287    #[inline]
288    pub(super) const fn from(ptr: NonNull<RefCounted<T>>) -> Self {
289        Self { ptr }
290    }
291
292    /// Returns a pointer to the [`RefCounted`].
293    #[inline]
294    pub(super) const fn underlying_ptr(&self) -> *const RefCounted<T> {
295        self.ptr.as_ptr()
296    }
297}
298
299impl<T> AsRef<T> for Shared<T> {
300    #[inline]
301    fn as_ref(&self) -> &T {
302        unsafe { &*self.ptr.as_ptr() }
303    }
304}
305
306impl<T> Clone for Shared<T> {
307    #[inline]
308    fn clone(&self) -> Self {
309        unsafe { (*self.ptr.as_ptr()).add_ref() }
310        Self { ptr: self.ptr }
311    }
312}
313
314impl<T> Deref for Shared<T> {
315    type Target = T;
316
317    #[inline]
318    fn deref(&self) -> &Self::Target {
319        self.as_ref()
320    }
321}
322
323impl<T> Drop for Shared<T> {
324    #[inline]
325    fn drop(&mut self) {
326        if unsafe { (*self.ptr.as_ptr()).drop_ref() } {
327            RefCounted::pass_to_collector(self.ptr.as_ptr());
328        }
329    }
330}
331
332impl<'g, T> TryFrom<Ptr<'g, T>> for Shared<T> {
333    type Error = Ptr<'g, T>;
334
335    #[inline]
336    fn try_from(ptr: Ptr<'g, T>) -> Result<Self, Self::Error> {
337        if let Some(shared) = ptr.get_shared() {
338            Ok(shared)
339        } else {
340            Err(ptr)
341        }
342    }
343}
344
345unsafe impl<T: Send> Send for Shared<T> {}
346
347unsafe impl<T: Send + Sync> Sync for Shared<T> {}
348
349impl<T: UnwindSafe> UnwindSafe for Shared<T> {}