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}