Skip to main content

manual_share/
shared_box.rs

1//! # Manually shared box
2//! ```
3//! use std::thread;
4//! use manual_share::SharedBox;
5//!
6//! let b = Box::new(13);
7//! let mut b = SharedBox::from_box(b);
8//!
9//! let br = b.borrow();
10//! let j1 = thread::spawn(move || {
11//!     println!("{}", br.get());
12//!     br
13//! });
14//!
15//! let br = b.borrow();
16//! let j2 = thread::spawn(move || {
17//!     println!("{}", br.get() + 1);
18//!     br
19//! });
20//!
21//! b.try_return(j1.join().unwrap()).unwrap();
22//! b.try_return(j2.join().unwrap()).unwrap();
23//!
24//! let b = b.try_into_box().unwrap();
25//! println!("{:?}", b);
26//! ```
27
28use std::ops::Deref;
29
30/// A structure owning the original `Box`
31/// which can be used to create multiple `SharedBoxRef` to send to other thread.
32/// It uses a counter to record the number of `SharedBoxRef` that has been created and not given back.
33///
34/// Dropping `SharedBox` without returning all `SharedBoxRef` that it has created leaks its heap memory.
35/// When **`panic-on-drop`** feature is enabled, it will panic:
36/// ```should_panic
37/// let r = {
38///     let mut b = manual_share::SharedBox::new(0);
39///     b.borrow()
40///
41///     // dropping SharedBox, panic here
42/// };
43/// println!("{}", r.get());
44/// ```
45///
46/// When all `SharedBoxRef` have been returned back to `SharedBox`,
47/// it will free and properly release its heap memory when `SharedBox` is dropped.
48/// ```
49/// let mut b = manual_share::SharedBox::new(0);
50/// let r = b.borrow();
51/// b.try_return(r).unwrap();
52///
53/// // Dropping SharedBox here will free the heap memory it owns. No panic will occur.
54/// ```
55#[derive(Debug)]
56pub struct SharedBox<T: ?Sized> {
57    borrow_count: usize,
58    ptr: *mut T,
59}
60
61impl<T> SharedBox<T> {
62    /// Create a `SharedBox` by creating a `Box` first,
63    /// then convert it to a `SharedBox`.
64    pub fn new(value: T) -> Self {
65        let b = Box::new(value);
66        Self::from_box(b)
67    }
68}
69
70impl<T: ?Sized> SharedBox<T> {
71    /// Create a `SharedBox` by consuming a `Box`.
72    pub fn from_box(unique: Box<T>) -> Self {
73        Self {
74            borrow_count: 0,
75            ptr: Box::into_raw(unique),
76        }
77    }
78
79    /// Create a `SharedBoxRef` and increase the borrow count.
80    /// # panics
81    /// Panics when borrow count overflows `usize`.
82    pub fn borrow(&mut self) -> SharedBoxRef<T> {
83        self.borrow_count = self.borrow_count.checked_add(1).unwrap();
84        SharedBoxRef { ptr: self.ptr }
85    }
86
87    /// Try to return back the `SharedBoxRef`.
88    /// Returns `Err` if the `SharedBoxRef` does not originate from the same `SharedBox`.
89    ///
90    /// Decrease the borrow count if not error occurs.
91    ///
92    /// ```
93    /// use manual_share::SharedBox;
94    ///
95    /// let mut b1 = SharedBox::from_box(Box::new(8));
96    /// let r1 = b1.borrow();
97    /// b1.try_return(r1).unwrap();
98    ///
99    /// let mut b2 = SharedBox::from_box(Box::new(9));
100    /// let r2 = b2.borrow();
101    ///
102    /// // Giving SharedBoxRef to the wrong SharedBox returns Err.
103    /// let r2 = b1.try_return(r2).unwrap_err();
104    ///
105    /// b2.try_return(r2).unwrap();
106    /// ```
107    pub fn try_return(&mut self, reference: SharedBoxRef<T>) -> Result<(), SharedBoxRef<T>> {
108        if !core::ptr::eq(self.ptr, reference.ptr) {
109            return Err(reference);
110        }
111
112        if size_of_val(unsafe { &*self.ptr }) == 0 {
113            // ZST types can have multiple allocations to the same address, so we need to check for overflow.
114            if let Some(new_count) = self.borrow_count.checked_sub(1) {
115                self.borrow_count = new_count;
116                let _ = core::mem::ManuallyDrop::new(reference);
117                Ok(())
118            } else {
119                Err(reference)
120            }
121        } else {
122            self.borrow_count -= 1;
123            let _ = core::mem::ManuallyDrop::new(reference);
124            Ok(())
125        }
126    }
127
128    /// Try to convert `Self` into a `Box` if all borrowed `SharedBoxRef` has been given back.
129    ///
130    /// Note that the returned error type is `Self`, dropping it may cause panic.
131    ///
132    /// ```
133    /// use manual_share::SharedBox;
134    ///
135    /// let b = Box::new(0);
136    /// let mut b = SharedBox::from_box(b);
137    ///
138    /// let r = b.borrow();
139    ///
140    /// // Try to convert to Box without returning all SharedBoxRef returns Err.
141    /// let mut b = b.try_into_box().unwrap_err();
142    ///
143    /// b.try_return(r).unwrap();
144    ///
145    /// let b = b.try_into_box().unwrap();
146    /// assert_eq!(b, Box::new(0));
147    /// ```
148    ///
149    pub fn try_into_box(self) -> Result<Box<T>, Self> {
150        if self.borrow_count > 0 {
151            Err(self)
152        } else {
153            let r = core::mem::ManuallyDrop::new(self);
154            Ok(unsafe { Box::from_raw(r.ptr) })
155        }
156    }
157    /// Directly get a reference to the value inside the `SharedBox`.
158    /// This use rust built-in lifetime check to ensure the reference is valid as long as the `SharedBox` is alive,
159    /// and has no runtime overhead.
160    pub fn get(&self) -> &T {
161        // SAFETY:
162        // The pointer is valid as long as the SharedBox is alive.
163        // All other references can only get immutable reference.
164        unsafe { &*self.ptr }
165    }
166
167    /// Get a mutable reference if borrow count is 0.
168    /// ```
169    /// use manual_share::SharedBox;
170    ///
171    /// let mut b = SharedBox::new(0);
172    /// let r = b.borrow();
173    ///
174    /// assert!(b.get_mut().is_none());
175    ///
176    /// b.try_return(r).unwrap();
177    ///
178    /// let r = b.get_mut().unwrap();
179    /// *r += 1;
180    ///
181    /// assert_eq!(*b, 1);
182    /// ```
183    pub fn get_mut(&mut self) -> Option<&mut T> {
184        if self.borrow_count == 0 {
185            // SAFETY:
186            // The pointer is valid as long as the SharedBox is alive.
187            // And there is no other references.
188            let r = unsafe { &mut *self.ptr };
189            Some(r)
190        } else {
191            None
192        }
193    }
194}
195
196impl<T: ?Sized> Deref for SharedBox<T> {
197    type Target = T;
198    fn deref(&self) -> &Self::Target {
199        self.get()
200    }
201}
202
203/// See <https://users.rust-lang.org/t/built-a-crate-to-safely-share-box-and-vec-manually/141138/25?u=newdino> for why it require `Sync`.
204unsafe impl<T: Send + Sync> Send for SharedBox<T> {}
205
206unsafe impl<T: Sync> Sync for SharedBox<T> {}
207
208impl<T: ?Sized> Drop for SharedBox<T> {
209    fn drop(&mut self) {
210        #[cfg(feature = "panic-on-drop")]
211        {
212            // Let user deal with other panics.
213            #[cfg(feature = "do-not-panic-when-panicking")]
214            if std::thread::panicking() {
215                return;
216            }
217
218            if self.borrow_count > 0 {
219                panic!("Dropping a SharedBox without giving back all SharedBoxRef")
220            }
221        }
222        // Only drops when there are no outstanding SharedBoxRef values to prevent use-after-free.
223        if self.borrow_count == 0 {
224            unsafe {
225                drop(Box::from_raw(self.ptr));
226            }
227        }
228    }
229}
230
231/// A Reference to `SharedBox` that can be sent to other threads.
232///
233/// Dropping a `SharedBoxRef` leaks the heap memory it points to.
234/// When **`panic-on-drop`** feature is enabled, dropping it will panic:
235/// ```should_panic
236/// let mut b = manual_share::SharedBox::new(1);
237/// b.borrow();
238///
239/// // forget SharedBox to make sure the panic is not caused by dropping it first.
240/// std::mem::forget(b);
241///
242/// // panic here due to dropping SharedBoxRef
243/// ```
244///
245/// Use `SharedBox::try_return` to consume it without causing panic.
246/// ```
247/// let mut b = manual_share::SharedBox::new(1);
248/// let r = b.borrow();
249/// b.try_return(r).unwrap();
250/// ```
251#[derive(Debug)]
252pub struct SharedBoxRef<T: ?Sized> {
253    ptr: *const T,
254}
255
256/// `SharedBoxRef` is like `&T`, which only requires `T: Sync` to implement `Send`.
257unsafe impl<T: Sync> Send for SharedBoxRef<T> {}
258unsafe impl<T: Sync> Sync for SharedBoxRef<T> {}
259
260impl<T: ?Sized> SharedBoxRef<T> {
261    /// Example usage:
262    /// ```
263    /// let mut b = manual_share::SharedBox::new(42);
264    /// let r = b.borrow();
265    /// let value = *r.get();
266    /// assert_eq!(value, 42);
267    ///
268    /// b.try_return(r).unwrap();
269    /// ```
270    ///
271    /// The reference got from this method has the same lifetime of the `SharedBoxRef`,
272    /// which means it will be invalidated after `SharedBoxRef` is given back to `SharedBox`:
273    /// ```compile_fail
274    /// let mut b = manual_share::SharedBox::new(42);
275    /// let br = b.borrow();
276    /// let r = br.get();
277    ///
278    /// b.try_return(br).unwrap();
279    ///
280    /// // r is no longer valid here.
281    /// println!("{}", r);
282    /// ```
283    pub fn get(&self) -> &T {
284        unsafe { &*self.ptr }
285    }
286}
287
288impl<T: ?Sized> Drop for SharedBoxRef<T> {
289    fn drop(&mut self) {
290        #[cfg(feature = "panic-on-drop")]
291        {
292            #[cfg(feature = "do-not-panic-when-panicking")]
293            // Let user deal with other panics.
294            if std::thread::panicking() {
295                return;
296            }
297
298            panic!("SharedBoxRef should not be dropped. Use SharedBox::try_return to consume it.");
299        }
300    }
301}
302
303impl<T: ?Sized> Deref for SharedBoxRef<T> {
304    type Target = T;
305    fn deref(&self) -> &Self::Target {
306        self.get()
307    }
308}
309
310#[cfg(test)]
311mod test {
312    use super::*;
313
314    #[test]
315    fn zst() {
316        let mut b1 = SharedBox::new(());
317        let mut b2 = SharedBox::new(());
318
319        let r11 = b1.borrow();
320        let r12 = b1.borrow();
321
322        let r2 = b2.borrow();
323
324        b1.try_return(r2).unwrap();
325
326        b1.try_return(r11).unwrap();
327        let r12 = b1.try_return(r12).unwrap_err();
328
329        b2.try_return(r12).unwrap();
330    }
331    #[test]
332    fn zst_dyn_trait() {
333        // When comparing wide pointers, both the address and the metadata are tested for equality.
334        // It should become impossible to return a ZST ref with one vtable to another SharedBox.
335
336        trait T1 {
337            fn f(&self) -> u32;
338        }
339
340        struct A;
341        impl T1 for A {
342            fn f(&self) -> u32 {
343                0
344            }
345        }
346
347        struct B;
348        impl T1 for B {
349            fn f(&self) -> u32 {
350                1
351            }
352        }
353
354        let b1: Box<dyn T1> = Box::new(A);
355        let b2: Box<dyn T1> = Box::new(B);
356
357        let mut b1 = SharedBox::from_box(b1);
358        let mut b2 = SharedBox::from_box(b2);
359
360        let r1 = b1.borrow();
361        let r2 = b2.borrow();
362
363        assert_eq!(r1.get().f(), 0);
364        assert_eq!(r2.get().f(), 1);
365
366        let r2 = b1.try_return(r2).unwrap_err();
367        let r1 = b2.try_return(r1).unwrap_err();
368
369        assert!(b1.try_return(r1).is_ok());
370        assert!(b2.try_return(r2).is_ok());
371    }
372
373    #[test]
374    fn zst_slice() {
375        // When comparing wide pointers, both the address and the metadata are tested for equality.
376        // It should become impossible to return a ZST slice with one length to SharedBox with another length.
377
378        let b1: Box<[()]> = Box::new([(); 1]);
379        let b2: Box<[()]> = Box::new([(); 2]);
380
381        let mut b1 = SharedBox::from_box(b1);
382        let mut b2 = SharedBox::from_box(b2);
383
384        let r1 = b1.borrow();
385        let r2 = b2.borrow();
386
387        let r2 = b1.try_return(r2).unwrap_err();
388        let r1 = b2.try_return(r1).unwrap_err();
389
390        assert!(b1.try_return(r1).is_ok());
391        assert!(b2.try_return(r2).is_ok());
392    }
393}