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