tinypointers/
sync.rs

1use std::{fmt::Debug, ops::Deref, sync::atomic::AtomicU32};
2
3use crate::TinyPtr;
4
5#[derive(Debug)]
6struct RefCounted<T> {
7    count: AtomicU32,
8    value: T,
9}
10
11#[derive(Debug)]
12/// A weak reference to a [`TinyArc`], which is a thread-safe reference-counting tiny pointer.
13/// Essentially, it is non owning, and can be upgraded to a [`TinyArc`] at any time to access the
14/// data.
15/// ## Example
16/// ```rust
17/// use tinypointers::TinyArc;
18///
19/// let owned = TinyArc::new(42);
20/// let non_owned = TinyArc::downgrade(&owned);
21/// assert_eq!(*owned, 42);
22/// assert_eq!(*non_owned.upgrade(), 42);
23/// ```
24pub struct TinyWeak<T>(TinyPtr<RefCounted<T>>);
25
26unsafe impl<T: Send + Sync> Send for TinyWeak<T> {}
27unsafe impl<T: Send + Sync> Sync for TinyWeak<T> {}
28
29impl<T> Clone for TinyWeak<T> {
30    fn clone(&self) -> Self {
31        Self(self.0)
32    }
33}
34
35crate::boxed::impl_traits!(TinyArc);
36
37impl<T> TinyWeak<T> {
38    /// Attempts to upgrade the `TinyWeak` pointer to an `TinyArc`, extending the lifetime of the
39    /// data if successful.
40    /// ## Example
41    /// ```rust
42    /// use tinypointers::TinyArc;
43    ///
44    /// let owned = TinyArc::new(42);
45    /// let non_owned = TinyArc::downgrade(&owned);
46    ///
47    /// drop(owned);
48    ///
49    /// let owned = non_owned.upgrade(); // Panics
50    /// ```
51    ///
52    /// ## Panics
53    /// This panics if the data has since been dropped. I.E. if the `TinyArc` count is zero.
54    pub fn upgrade(&self) -> TinyArc<T> {
55        let arc = TinyArc(self.0);
56        TinyArc::increase_count(&arc);
57        arc
58    }
59}
60
61/// A thread-safe reference-counting tiny pointer. As with all types of this crate, memory is
62/// allocated on the heap. It is equivalent to [`std::sync::Arc`].
63///
64/// ```rust
65/// use tinypointers::TinyArc;
66///
67/// let x = TinyArc::new(42);
68/// let y = x.clone();
69/// println!("{}", *x); // prints 42
70/// println!("{}", *y); // prints 42
71/// // both x and y point to the same memory location
72/// ```
73pub struct TinyArc<T>(TinyPtr<RefCounted<T>>);
74
75unsafe impl<T: Send + Sync> Send for TinyArc<T> {}
76unsafe impl<T: Send + Sync> Sync for TinyArc<T> {}
77
78impl<T> TinyArc<T> {
79    /// Allocates memory on the heap and then places `value` into it.
80    /// ## Example
81    /// ```rust
82    /// use tinypointers::TinyArc;
83    ///
84    /// let x = TinyArc::new(42);
85    /// ```
86    pub fn new(value: T) -> Self {
87        Self(TinyPtr::new(RefCounted {
88            count: AtomicU32::new(1),
89            value,
90        }))
91    }
92    /// Constructs a new `TinyArc<T>` while giving you a `TinyWeak<T>` to the allocation, to allow
93    /// you to construct a `T` which holds a weak pointer to itself.
94    ///
95    /// `new_cyclic` first allocates the managed allocation for the `TinyArc<T>`,
96    /// then calls your closure, giving it a `TinyWeak<T>` to this allocation,
97    /// and only afterwards completes the construction of the `TinyArc<T>` by placing
98    /// the `T` returned from your closure into the allocation.
99    ///
100    /// ## Panic
101    /// Keep in mind that the `TinyArc<T>` is not fully constructed until `TinyArc<T>::new_cyclic`
102    /// returns. Calling [`TinyWeak::upgrade`] will cause a panic.
103    pub fn new_cyclic<F>(data_fn: F) -> Self where F: FnOnce(TinyWeak<T>) -> T {
104        let mut ptr = TinyPtr::new(RefCounted {
105            count: AtomicU32::new(0),
106            value: unsafe { std::mem::MaybeUninit::<T>::uninit().assume_init() },
107        });
108        let data = data_fn(TinyWeak(ptr));
109        unsafe {
110            let ptr = ptr.get_mut();
111            std::ptr::addr_of_mut!(ptr.value).write(data);
112        }
113        let this = Self(ptr);
114        Self::increase_count(&this);
115        this
116    }
117    /// Returns a raw pointer to the inner value.
118    ///
119    /// The pointer will be valid for as long as there are strong references to this allocation.
120    pub fn as_ptr(this: &Self) -> *const T {
121        &this.get().value
122    }
123    /// Checks whether the two `TinyArc`s point to the same allocation.
124    pub fn ptr_eq(this: &Self, other: &Self) -> bool {
125        this.0.id() == other.0.id()
126    }
127    /// Creates a [`TinyWeak`] pointer to this allocation.
128    ///
129    /// Weak references do not keep the allocation alive, and cannot access the inner value.
130    pub fn downgrade(this: &Self) -> TinyWeak<T> {
131        TinyWeak(this.0)
132    }
133
134    // internal apis
135
136    fn get(&self) -> &RefCounted<T> {
137        unsafe { &*self.0.get() }
138    }
139    fn increase_count(this: &Self) -> u32 {
140        this.get()
141            .count
142            .fetch_add(1, std::sync::atomic::Ordering::Relaxed)
143    }
144    fn decrease_count(this: &Self) -> u32 {
145        this.get()
146            .count
147            .fetch_sub(1, std::sync::atomic::Ordering::Relaxed)
148    }
149}
150
151impl<T: Debug> Debug for TinyArc<T> {
152    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153        f.debug_struct("TinyArc")
154            .field("refcount", self.get())
155            .finish()
156    }
157}
158
159impl<T> Deref for TinyArc<T> {
160    type Target = T;
161    fn deref(&self) -> &Self::Target {
162        let refcounted = self.get();
163        if refcounted.count.load(std::sync::atomic::Ordering::Relaxed) == 0 {
164            panic!("Attempted to dereference a TinyArc before it was built")
165        }
166        &refcounted.value
167    }
168}
169
170impl<T> Clone for TinyArc<T> {
171    fn clone(&self) -> Self {
172        Self::increase_count(self);
173        Self(self.0)
174    }
175}
176
177impl<T> std::ops::Drop for TinyArc<T> {
178    fn drop(&mut self) {
179        let owners = Self::decrease_count(self);
180        if owners == 1 {
181            // Drop the value if we're the last owner
182            self.0.take();
183        }
184    }
185}
186
187#[cfg(test)]
188mod tests {
189
190    use std::sync::atomic::AtomicBool;
191
192    use super::*;
193
194    use crate::tests::{*, make_drop_indicator};
195
196    #[test]
197    fn multiple_thread_access() {
198        make_drop_indicator!(__ind, p2, 42);
199        let p2 = TinyArc::new(p2);
200        let p1 = p2.clone();
201        let t1 = std::thread::spawn(move || {
202            assert_eq!(*p1, 42);
203        });
204        let t2 = std::thread::spawn(move || {
205            assert_eq!(*p2, 42);
206        });
207        t1.join().unwrap();
208        t2.join().unwrap();
209        assert_dropped!(__ind);
210    }
211    #[test]
212    fn assert_optimization_test() {
213        assert_eq!(
214            std::mem::size_of::<Option<TinyArc<u8>>>(),
215            std::mem::size_of::<TinyArc<u8>>()
216        );
217    }
218
219    #[test]
220    fn single_arc_test() {
221        make_drop_indicator!(__ind, b, 42);
222        let b = TinyArc::new(b);
223        assert_eq!(*b, 42);
224        std::mem::drop(b);
225        assert_dropped!(__ind)
226    }
227
228    #[test]
229    #[cfg_attr(feature = "1byteid", ignore = "uses too much memory")]
230    fn multiple_arc_test() {
231        for i in 0..100 {
232            make_drop_indicator!(__ind, val, i);
233            {
234                let b = TinyArc::new(val);
235                assert_eq!(*b, i);
236            }
237            assert_dropped!(__ind)
238        }
239    }
240
241    #[test]
242    fn multiple_refs_test() {
243        make_drop_indicator!(__ind, v, 30);
244        let i = TinyArc::new(v);
245        for _x in 0..200 {
246            let j = i.clone();
247            assert_eq!(*j, 30);
248        }
249        std::mem::drop(i);
250        assert_dropped!(__ind)
251    }
252
253    #[test]
254    fn make_cyclic_test() {
255        #[derive(Debug)]
256        struct Narcissus {
257            _drop_indicator: DropIndicator<()>,
258            self_: TinyWeak<Narcissus>,
259        }
260
261        make_drop_indicator!(__ind, ind, ());
262        let narc = TinyArc::new_cyclic(|weak| {
263            Narcissus{self_: weak, _drop_indicator: ind}
264        });
265
266        assert!(TinyArc::ptr_eq(&narc, &narc.self_.upgrade()));
267        std::mem::drop(narc);
268        assert_dropped!(__ind);
269    }
270
271    #[test]
272    #[should_panic]
273    fn make_cyclic_panic_test() {
274        TinyArc::<()>::new_cyclic(|weak| {
275            weak.upgrade();
276        });
277    }
278}