#![cfg_attr(feature = "cyclic", feature(layout_for_ptr))]
mod internals;
use core::fmt;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
use std::error;
use internals::{RefBoxHeap, RefBoxHeapInner, Status, WeakCount};
#[macro_export]
macro_rules! coerce {
($ref_box:expr => $into_type:ty) => {{
let __raw = $crate::RefBox::into_raw($ref_box);
let __out: $crate::RefBox<$into_type> = unsafe { $crate::RefBox::from_raw(__raw) };
__out
}};
}
#[macro_export]
macro_rules! coerce_weak {
($weak:expr => $into_type:ty) => {{
let __raw = $crate::Weak::into_raw($weak);
let __out: $crate::Weak<$into_type> = unsafe { $crate::Weak::from_raw(__raw) };
__out
}};
}
pub struct RefBox<T: ?Sized> {
ptr: NonNull<RefBoxHeap<T>>,
_p: PhantomData<RefBoxHeap<T>>,
}
impl<T: ?Sized> Drop for RefBox<T> {
fn drop(&mut self) {
unsafe { internals::drop_ref_box(self.ptr) };
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for RefBox<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_borrow_mut() {
Ok(this) => f
.debug_tuple("RefBox")
.field(&self.ptr)
.field(&&*this)
.finish(),
Err(_) => f.debug_tuple("RefBox").field(&self.ptr).finish(),
}
}
}
impl<T: ?Sized> PartialEq<Weak<T>> for RefBox<T> {
fn eq(&self, other: &Weak<T>) -> bool {
other.is(self)
}
}
impl<T: Default> Default for RefBox<T> {
#[inline]
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> From<T> for RefBox<T> {
#[inline]
fn from(val: T) -> Self {
Self::new(val)
}
}
impl<T> RefBox<T> {
pub fn new(value: T) -> Self {
internals::new_ref_box(value)
}
#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
pub fn new_cyclic<F: FnOnce(&Weak<T>) -> T>(op: F) -> Self {
internals::new_cyclic_ref_box(op)
}
}
impl<T: ?Sized> RefBox<T> {
#[inline(always)]
fn heap(&self) -> &RefBoxHeapInner {
let ptr = self.ptr.as_ptr();
unsafe { &(*ptr).inner }
}
#[inline]
pub fn try_borrow_mut(&self) -> Result<Borrow<T>, BorrowError> {
self.try_borrow_mut_or_else(|| BorrowError::Borrowed)
}
pub fn try_borrow_mut_or_else<E>(
&self,
err_borrowed: impl FnOnce() -> E,
) -> Result<Borrow<T>, E> {
match self.heap().status() {
Status::Available => Ok(unsafe { Borrow::new(self.ptr.as_ref()) }),
Status::Borrowed => Err(err_borrowed()),
Status::Dropped | Status::DroppedWhileBorrowed => unreachable!(),
}
}
pub fn try_access_mut<R, F: FnOnce(&mut T) -> R>(&self, op: F) -> Result<R, BorrowError> {
let mut borrow = self.try_borrow_mut()?;
Ok(op(&mut *borrow))
}
pub fn downgrade(&self) -> Weak<T> {
self.heap().increase_weak_count();
Weak { ptr: self.ptr }
}
pub fn try_downgrade(&self) -> Option<Weak<T>> {
if self.heap().try_increase_weak_count() {
Some(Weak { ptr: self.ptr })
} else {
None
}
}
pub fn weak_count(&self) -> WeakCount {
self.heap().weak_count()
}
pub unsafe fn get_unchecked(&self) -> &T {
unsafe { self.ptr.as_ref().data_ref() }
}
pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
unsafe { self.ptr.as_ref().data_mut() }
}
pub fn as_ptr(&self) -> *const T {
let ptr = self.ptr.as_ptr();
unsafe { &raw const (*ptr).data as *const T }
}
pub fn into_raw(self) -> *mut RefBoxHeap<T> {
let ptr = self.ptr.as_ptr();
std::mem::forget(self);
ptr
}
pub unsafe fn from_raw(ptr: *mut RefBoxHeap<T>) -> Self {
Self {
ptr: unsafe { NonNull::new_unchecked(ptr) },
_p: PhantomData,
}
}
pub unsafe fn cast<U>(self) -> RefBox<U> {
let raw_ptr = self.into_raw();
unsafe { RefBox::from_raw(raw_ptr as _) }
}
#[cfg(test)]
fn status(&self) -> Status {
self.heap().status()
}
}
pub struct Weak<T: ?Sized> {
ptr: NonNull<RefBoxHeap<T>>,
}
impl<T: ?Sized> Drop for Weak<T> {
fn drop(&mut self) {
unsafe { internals::drop_weak(self.ptr) };
}
}
impl<T: ?Sized> Clone for Weak<T> {
fn clone(&self) -> Self {
self.heap().increase_weak_count();
Weak { ptr: self.ptr }
}
}
impl<T: ?Sized> fmt::Debug for Weak<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Weak").field(&self.ptr).finish()
}
}
impl<T: ?Sized> PartialEq for Weak<T> {
fn eq(&self, other: &Self) -> bool {
std::ptr::addr_eq(self.ptr.as_ptr(), other.ptr.as_ptr())
}
}
impl<T: ?Sized> PartialEq<RefBox<T>> for Weak<T> {
fn eq(&self, other: &RefBox<T>) -> bool {
self.is(other)
}
}
impl<T: ?Sized> Eq for Weak<T> {}
impl<T: ?Sized> Weak<T> {
#[inline(always)]
fn heap(&self) -> &RefBoxHeapInner {
let ptr = self.ptr.as_ptr();
unsafe { &(*ptr).inner }
}
#[inline]
pub fn try_borrow_mut(&self) -> Result<Borrow<T>, BorrowError> {
self.try_borrow_mut_or_else(|| BorrowError::Borrowed, || BorrowError::Dropped)
}
pub fn try_borrow_mut_or_else<E>(
&self,
err_borrowed: impl FnOnce() -> E,
err_dropped: impl FnOnce() -> E,
) -> Result<Borrow<T>, E> {
match self.heap().status() {
Status::Available => Ok(unsafe { Borrow::new(self.ptr.as_ref()) }),
Status::Borrowed => Err(err_borrowed()),
Status::Dropped | Status::DroppedWhileBorrowed => Err(err_dropped()),
}
}
pub fn try_access_mut<R, F: FnOnce(&mut T) -> R>(&self, op: F) -> Result<R, BorrowError> {
let mut borrow = self.try_borrow_mut()?;
Ok(op(&mut *borrow))
}
pub fn try_clone(&self) -> Option<Weak<T>> {
if self.heap().try_increase_weak_count() {
Some(Weak { ptr: self.ptr })
} else {
None
}
}
pub fn weak_count(&self) -> WeakCount {
self.heap().weak_count()
}
pub fn is_alive(&self) -> bool {
self.heap().is_alive()
}
pub fn is_borrowed(&self) -> bool {
self.heap().is_borrowed()
}
pub fn is(&self, owner: &RefBox<T>) -> bool {
std::ptr::addr_eq(self.ptr.as_ptr(), owner.ptr.as_ptr())
}
pub unsafe fn get_unchecked(&self) -> &T {
unsafe { self.ptr.as_ref().data_ref() }
}
pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
unsafe { self.ptr.as_ref().data_mut() }
}
pub fn as_ptr(&self) -> *const T {
let ptr = self.ptr.as_ptr();
unsafe { &raw const (*ptr).data as *const T }
}
pub fn into_raw(self) -> *mut RefBoxHeap<T> {
let ptr = self.ptr.as_ptr();
std::mem::forget(self);
ptr
}
pub unsafe fn from_raw(ptr: *mut RefBoxHeap<T>) -> Self {
let ptr = unsafe { NonNull::new_unchecked(ptr) };
Self { ptr }
}
pub unsafe fn cast<U>(self) -> Weak<U> {
let raw_ptr = self.into_raw();
unsafe { Weak::from_raw(raw_ptr as _) }
}
#[cfg(test)]
fn status(&self) -> Status {
self.heap().status()
}
}
pub struct Borrow<'ptr, T: ?Sized> {
pub(crate) heap: &'ptr RefBoxHeap<T>,
pub(crate) _p: PhantomData<&'ptr mut T>,
}
impl<'ptr, T: ?Sized> Borrow<'ptr, T> {
#[inline]
unsafe fn new(heap: &'ptr RefBoxHeap<T>) -> Self {
heap.inner.start_borrow();
Self {
heap,
_p: PhantomData,
}
}
}
impl<'ptr, T: ?Sized> Drop for Borrow<'ptr, T> {
fn drop(&mut self) {
unsafe { internals::drop_borrow(self.heap) };
}
}
impl<'ptr, T: ?Sized> Deref for Borrow<'ptr, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.heap.data_ref() }
}
}
impl<'ptr, T: ?Sized> DerefMut for Borrow<'ptr, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.heap.data_mut() }
}
}
impl<'ptr, T: ?Sized + fmt::Debug> fmt::Debug for Borrow<'ptr, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Borrow").field(&self.deref()).finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BorrowError {
Borrowed,
Dropped,
}
impl fmt::Display for BorrowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BorrowError::Borrowed => write!(f, "already borrowed"),
BorrowError::Dropped => write!(f, "owner dropped"),
}
}
}
impl error::Error for BorrowError {}
#[cfg(test)]
mod tests {
use std::cell::Cell;
use std::panic;
use std::rc::Rc;
use crate::RefBox;
use crate::internals::{RefBoxHeap, Status};
#[test]
fn ref_box_new() {
let ref_box = RefBox::new(123456);
assert_eq!(unsafe { *ref_box.get_unchecked() }, 123456);
drop(ref_box);
}
#[test]
fn ref_box_new_weak_count() {
let ref_box = RefBox::new(123456);
assert_eq!(ref_box.weak_count(), 0);
drop(ref_box);
}
#[test]
#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
fn ref_box_new_cyclic() {
let ref_box = RefBox::new_cyclic(|_weak| 13579);
assert_eq!(unsafe { *ref_box.get_unchecked() }, 13579);
drop(ref_box);
}
#[test]
#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
fn ref_box_new_cyclic_weak_count() {
let ref_box = RefBox::new_cyclic(|weak| {
assert_eq!(weak.weak_count(), 1);
});
assert_eq!(ref_box.weak_count(), 0);
drop(ref_box);
}
#[test]
#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
fn ref_box_new_cyclic_ptr_eq() {
let mut out_weak = None;
let ref_box = RefBox::new_cyclic(|weak| out_weak = Some(weak.clone()));
assert_eq!(ref_box.ptr.as_ptr(), out_weak.unwrap().ptr.as_ptr());
drop(ref_box);
}
#[test]
#[should_panic]
#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
fn ref_box_new_cyclic_does_not_leak() {
let ref_box = RefBox::new_cyclic(|_weak| {
panic!("panic in closure!");
});
drop(ref_box);
}
#[test]
fn downgrade_ptr_eq() {
let val = 123456;
let ref_box = RefBox::new(val);
let weak = RefBox::downgrade(&ref_box);
assert_eq!(ref_box.ptr.as_ptr(), weak.ptr.as_ptr());
}
#[test]
fn downgrade_increases_refcount() {
let val = 123456;
let ref_box = RefBox::new(val);
assert_eq!(ref_box.weak_count(), 0);
let weak = RefBox::downgrade(&ref_box);
assert_eq!(ref_box.weak_count(), 1);
assert_eq!(weak.weak_count(), 1);
}
#[test]
fn downgrade_panics_on_max() {
let val = 123456;
let ref_box = RefBox::new(val);
ref_box.heap().set_weak_count(u32::MAX);
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let weak = RefBox::downgrade(&ref_box);
drop(weak);
}));
assert!(result.is_err());
ref_box.heap().set_weak_count(0);
drop(ref_box);
}
#[test]
fn try_downgrade_returns_none_on_max() {
let val = 123456;
let ref_box = RefBox::new(val);
ref_box.heap().set_weak_count(u32::MAX);
let weak = RefBox::try_downgrade(&ref_box);
assert!(weak.is_none());
drop(weak);
ref_box.heap().set_weak_count(0);
drop(ref_box);
}
#[test]
fn cloning_weak_increases_weak_count() {
let val = 123456;
let ref_box = RefBox::new(val);
assert_eq!(ref_box.weak_count(), 0);
let weak = RefBox::downgrade(&ref_box);
assert_eq!(ref_box.weak_count(), 1);
let weak2 = weak.clone();
assert_eq!(ref_box.weak_count(), 2);
drop(weak2);
drop(weak);
}
#[test]
fn cloning_weak_panics_on_max() {
let ref_box = RefBox::new(123456);
let weak = RefBox::downgrade(&ref_box);
weak.heap().set_weak_count(u32::MAX);
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let weak2 = weak.clone();
drop(weak2);
}));
assert!(result.is_err());
drop(weak);
ref_box.heap().set_weak_count(0);
drop(ref_box);
}
#[test]
fn dropping_weak_decreases_weak_count() {
let val = 123456;
let ref_box = RefBox::new(val);
let weak = RefBox::downgrade(&ref_box);
assert_eq!(ref_box.weak_count(), 1);
drop(weak);
assert_eq!(ref_box.weak_count(), 0);
}
#[test]
fn owner_is_alive() {
let val = 123456;
let ref_box = RefBox::new(val);
let weak = RefBox::downgrade(&ref_box);
assert_eq!(weak.is_alive(), true);
drop(ref_box);
}
#[test]
fn owner_is_not_alive_after_dropped() {
let val = 123456;
let ref_box = RefBox::new(val);
let weak = RefBox::downgrade(&ref_box);
assert_eq!(weak.is_alive(), true);
drop(ref_box);
assert_eq!(weak.is_alive(), false);
}
#[test]
fn dropping_ref_box_drops_data() {
struct DropThing(Rc<Cell<bool>>);
impl Drop for DropThing {
fn drop(&mut self) {
self.0.set(true);
}
}
let drop_checker = Rc::new(Cell::new(false));
let ref_box = RefBox::new(DropThing(drop_checker.clone()));
assert_eq!(drop_checker.get(), false);
drop(ref_box);
assert_eq!(drop_checker.get(), true);
}
#[test]
fn dropping_rc_with_weak_refs_drops_data() {
struct DropThing(Rc<Cell<bool>>);
impl Drop for DropThing {
fn drop(&mut self) {
self.0.set(true);
}
}
let drop_checker = Rc::new(Cell::new(false));
let ref_box = RefBox::new(DropThing(drop_checker.clone()));
let weak = RefBox::downgrade(&ref_box);
assert_eq!(drop_checker.get(), false);
drop(ref_box);
assert_eq!(drop_checker.get(), true);
drop(weak);
}
#[test]
fn dropping_weak_does_not_drop_data() {
struct DropThing(Rc<Cell<bool>>);
impl Drop for DropThing {
fn drop(&mut self) {
self.0.set(true);
}
}
let drop_checker = Rc::new(Cell::new(false));
let ref_box = RefBox::new(DropThing(drop_checker.clone()));
let weak = RefBox::downgrade(&ref_box);
assert_eq!(drop_checker.get(), false);
drop(weak);
assert_eq!(drop_checker.get(), false);
drop(ref_box);
assert_eq!(drop_checker.get(), true);
}
#[test]
fn owner_is_borrowable() {
let ref_box = RefBox::new(123456);
let borrow = ref_box.try_borrow_mut_or_else(|| "was borrowed");
assert!(borrow.is_ok());
drop(borrow);
drop(ref_box);
}
#[test]
fn owner_is_not_borrowable_twice() {
let ref_box = RefBox::new(123456);
let borrow = ref_box.try_borrow_mut_or_else(|| "was borrowed");
assert!(borrow.is_ok());
let borrow2 = ref_box.try_borrow_mut_or_else(|| "was borrowed");
assert!(matches!(borrow2, Err("was borrowed")));
drop(borrow);
drop(borrow2);
drop(ref_box);
}
#[test]
fn weak_is_borrowable() {
let ref_box = RefBox::new(123456);
let weak = RefBox::downgrade(&ref_box);
let borrow = weak.try_borrow_mut_or_else(|| "was borrowed", || "was dropped");
assert!(matches!(borrow, Ok(_)));
drop(borrow);
drop(weak);
drop(ref_box);
}
#[test]
fn weak_is_not_borrowable_twice() {
let ref_box = RefBox::new(123456);
let weak = RefBox::downgrade(&ref_box);
let borrow = weak.try_borrow_mut_or_else(|| "was borrowed", || "was dropped");
assert!(matches!(borrow, Ok(_)));
let borrow2 = weak.try_borrow_mut_or_else(|| "was borrowed", || "was dropped");
assert!(matches!(borrow2, Err("was borrowed")));
drop(borrow);
drop(borrow2);
drop(weak);
drop(ref_box);
}
#[test]
fn weak_is_not_borrowable_if_owner_borrowed() {
let ref_box = RefBox::new(123456);
let weak = RefBox::downgrade(&ref_box);
let borrow = ref_box.try_borrow_mut_or_else(|| "was borrowed");
assert!(matches!(borrow, Ok(_)));
let borrow2 = weak.try_borrow_mut_or_else(|| "was borrowed", || "was dropped");
assert!(matches!(borrow2, Err("was borrowed")));
drop(borrow);
drop(borrow2);
drop(weak);
drop(ref_box);
}
#[test]
fn weak_is_not_borrowable_if_owner_dropped() {
let ref_box = RefBox::new(123456);
let weak = RefBox::downgrade(&ref_box);
drop(ref_box);
let borrow = weak.try_borrow_mut_or_else(|| "was borrowed", || "was dropped");
assert!(matches!(borrow, Err("was dropped")));
drop(borrow);
drop(weak);
}
#[test]
fn dropping_owner_while_borrowed_delays_drops() {
let ref_box = RefBox::new(123456);
let weak = RefBox::downgrade(&ref_box);
let borrow = weak.try_borrow_mut_or_else(|| "was borrowed", || "was dropped");
assert_eq!(weak.status(), Status::Borrowed);
drop(ref_box);
assert_eq!(weak.status(), Status::DroppedWhileBorrowed);
drop(borrow);
assert_eq!(weak.status(), Status::Dropped);
drop(weak);
}
#[test]
fn borrowing_changes_status() {
let ref_box = RefBox::new(123456);
assert_eq!(ref_box.status(), Status::Available);
let borrow = ref_box.try_borrow_mut_or_else(|| "was borrowed");
assert_eq!(ref_box.status(), Status::Borrowed);
drop(borrow);
assert_eq!(ref_box.status(), Status::Available);
drop(ref_box);
}
#[test]
fn dropping_owner_changes_status() {
let ref_box = RefBox::new(123456);
let weak = RefBox::downgrade(&ref_box);
assert_eq!(weak.status(), Status::Available);
drop(ref_box);
assert_eq!(weak.status(), Status::Dropped);
drop(weak);
}
#[test]
#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
fn borrowing_in_cyclic_fails() {
let ref_box = RefBox::new_cyclic(|weak| {
let borrow = weak.try_borrow_mut_or_else(|| "was borrowed", || "was dropped");
assert!(borrow.is_err());
});
drop(ref_box);
}
#[test]
#[cfg(any(feature = "cyclic", feature = "cyclic_stable"))]
fn cloning_weak_in_cyclic_increases_refcount() {
let ref_box = RefBox::new_cyclic(|weak| {
let weak2 = weak.clone();
assert_eq!(weak.weak_count(), 2);
drop(weak2);
});
drop(ref_box);
}
#[test]
fn calling_getters_while_having_mutable_ref() {
let ref_box = RefBox::new(123456);
let mut borrow = ref_box.try_borrow_mut_or_else(|| "was borrowed").unwrap();
let mut_ref = &mut *borrow;
assert_eq!(ref_box.weak_count(), 0);
*mut_ref = 654321;
let weak = RefBox::downgrade(&ref_box);
assert_eq!(ref_box.weak_count(), 1);
*mut_ref = 13579;
drop(borrow);
drop(weak);
drop(ref_box);
}
#[test]
fn single_ref_boxes_are_distinct() {
let ref_box_1 = RefBox::new(123456);
let ref_box_2 = RefBox::new(654321);
let borrow1 = ref_box_1.try_borrow_mut().unwrap();
let borrow2 = ref_box_2.try_borrow_mut().unwrap();
assert_ne!(*borrow1, *borrow2);
assert_eq!(*borrow1, 123456);
assert_eq!(*borrow2, 654321);
drop(borrow1);
drop(borrow2);
drop(ref_box_1);
drop(ref_box_2);
}
#[test]
fn ref_box_as_ptr() {
let ref_box = RefBox::new(123456);
let ptr = ref_box.as_ptr();
let heap = unsafe { ref_box.ptr.as_ref() };
let real_ptr = heap.data.get();
assert_eq!(ptr, real_ptr);
drop(ref_box);
}
#[test]
fn weak_as_ptr() {
let ref_box = RefBox::new(123456);
let weak = RefBox::downgrade(&ref_box);
let ptr = weak.as_ptr();
let heap = unsafe { weak.ptr.as_ref() };
let real_ptr = heap.data.get();
assert_eq!(ptr, real_ptr);
drop(weak);
drop(ref_box);
}
#[test]
#[cfg(not(feature = "cyclic_stable"))]
fn heap_overhead() {
let layout = std::alloc::Layout::new::<RefBoxHeap<()>>();
assert_eq!(layout.size(), 8);
}
#[test]
#[cfg(feature = "cyclic_stable")]
#[cfg(any(target_pointer_width = "16"))]
fn heap_overhead_cyclic_stable_16bit() {
let layout = std::alloc::Layout::new::<RefBoxHeap<()>>();
assert_eq!(layout.size(), 12);
}
#[test]
#[cfg(feature = "cyclic_stable")]
#[cfg(any(target_pointer_width = "32"))]
fn heap_overhead_cyclic_stable_32bit() {
let layout = std::alloc::Layout::new::<RefBoxHeap<()>>();
assert_eq!(layout.size(), 16);
}
#[test]
#[cfg(feature = "cyclic_stable")]
#[cfg(any(target_pointer_width = "64"))]
fn heap_overhead_cyclic_stable_64bit() {
let layout = std::alloc::Layout::new::<RefBoxHeap<()>>();
assert_eq!(layout.size(), 24);
}
}