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}