unsync/bi_rc.rs
1//! A bi-cell, also known as "why the heck is this permitted?".
2//!
3//! This is a very simple reference-counted data structure who's purpose is
4//! exactly two things:
5//! * Ensure that the guarded value is not de-allocated until all live
6//! references of [BiRc] have been dropped.
7//! * Be able to flag when any of the two references of [BiRc] have been
8//! dropped.
9//!
10//! Now it's true that this API is roughly available through [Rc][std::rc::Rc],
11//! but it would be more awkward to wrap to use correctly.
12
13use std::cell::UnsafeCell;
14use std::ptr::NonNull;
15
16struct Inner<T> {
17 /// The interior value being reference counted.
18 value: UnsafeCell<T>,
19 /// How many users we currently have.
20 count: usize,
21}
22
23/// A simple `!Send` reference counted container which can be held at exactly
24/// two places.
25///
26/// The wrapped reference can be always unsafely accessed with an indication of
27/// whether both references are alive or not at the same time through
28/// [BiRc::get_mut_unchecked].
29pub struct BiRc<T> {
30 inner: NonNull<Inner<T>>,
31}
32
33impl<T> BiRc<T> {
34 /// Construct a new reference counted container.
35 pub(crate) fn new(value: T) -> (Self, Self) {
36 let inner = NonNull::from(Box::leak(Box::new(Inner {
37 value: UnsafeCell::new(value),
38 count: 2,
39 })));
40
41 (Self { inner }, Self { inner })
42 }
43
44 /// Get the interior value and indicates with a boolean if both ends of this
45 /// value are alive.
46 ///
47 /// # Safety
48 ///
49 /// Caller must ensure that the reference returned by `get_mut_unchecked` is
50 /// only used by one caller at the same time.
51 pub unsafe fn get_mut_unchecked(&self) -> (&mut T, bool) {
52 let inner = unsafe { &mut (*self.inner.as_ptr()) };
53 (inner.value.get_mut(), inner.count == 2)
54 }
55}
56
57impl<T> Drop for BiRc<T> {
58 fn drop(&mut self) {
59 unsafe {
60 let inner = self.inner.as_ptr();
61 let count = (*inner).count.wrapping_sub(1);
62 (*inner).count = count;
63
64 // De-allocate interior structure.
65 if count == 0 {
66 let _ = Box::from_raw(inner);
67 }
68 }
69 }
70}