use std::ops::Deref;
#[derive(Debug)]
pub struct SharedBox<T: ?Sized> {
borrow_count: usize,
ptr: *mut T,
}
impl<T> SharedBox<T> {
pub fn new(value: T) -> Self {
let b = Box::new(value);
Self::from_box(b)
}
}
impl<T: ?Sized> SharedBox<T> {
pub fn from_box(unique: Box<T>) -> Self {
Self {
borrow_count: 0,
ptr: Box::into_raw(unique),
}
}
pub fn borrow(&mut self) -> SharedBoxRef<T> {
self.borrow_count = self.borrow_count.checked_add(1).unwrap();
SharedBoxRef { ptr: self.ptr }
}
pub fn try_return(&mut self, reference: SharedBoxRef<T>) -> Result<(), SharedBoxRef<T>> {
if !core::ptr::eq(self.ptr, reference.ptr) {
return Err(reference);
}
if size_of_val(unsafe { &*self.ptr }) == 0 {
if let Some(new_count) = self.borrow_count.checked_sub(1) {
self.borrow_count = new_count;
let _ = core::mem::ManuallyDrop::new(reference);
Ok(())
} else {
Err(reference)
}
} else {
self.borrow_count -= 1;
let _ = core::mem::ManuallyDrop::new(reference);
Ok(())
}
}
pub fn try_into_box(self) -> Result<Box<T>, Self> {
if self.borrow_count > 0 {
Err(self)
} else {
let r = core::mem::ManuallyDrop::new(self);
Ok(unsafe { Box::from_raw(r.ptr) })
}
}
pub fn get(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T: ?Sized> Deref for SharedBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.get()
}
}
unsafe impl<T: Send + Sync> Send for SharedBox<T> {}
unsafe impl<T: Sync> Sync for SharedBox<T> {}
impl<T: ?Sized> Drop for SharedBox<T> {
fn drop(&mut self) {
#[cfg(feature = "panic-on-drop")]
{
#[cfg(feature = "do-not-panic-when-panicking")]
if std::thread::panicking() {
return;
}
if self.borrow_count > 0 {
panic!("Dropping a SharedBox without giving back all SharedBoxRef")
}
}
if self.borrow_count == 0 {
unsafe {
drop(Box::from_raw(self.ptr));
}
}
}
}
#[derive(Debug)]
pub struct SharedBoxRef<T: ?Sized> {
ptr: *const T,
}
unsafe impl<T: Sync> Send for SharedBoxRef<T> {}
unsafe impl<T: Sync> Sync for SharedBoxRef<T> {}
impl<T: ?Sized> SharedBoxRef<T> {
pub fn get(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T: ?Sized> Drop for SharedBoxRef<T> {
fn drop(&mut self) {
#[cfg(feature = "panic-on-drop")]
{
#[cfg(feature = "do-not-panic-when-panicking")]
if std::thread::panicking() {
return;
}
panic!("SharedBoxRef should not be dropped. Use SharedBox::try_return to consume it.");
}
}
}
impl<T: ?Sized> Deref for SharedBoxRef<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.get()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn zst() {
let mut b1 = SharedBox::new(());
let mut b2 = SharedBox::new(());
let r11 = b1.borrow();
let r12 = b1.borrow();
let r2 = b2.borrow();
b1.try_return(r2).unwrap();
b1.try_return(r11).unwrap();
let r12 = b1.try_return(r12).unwrap_err();
b2.try_return(r12).unwrap();
}
#[test]
fn zst_dyn_trait() {
trait T1 {
fn f(&self) -> u32;
}
struct A;
impl T1 for A {
fn f(&self) -> u32 {
0
}
}
struct B;
impl T1 for B {
fn f(&self) -> u32 {
1
}
}
let b1: Box<dyn T1> = Box::new(A);
let b2: Box<dyn T1> = Box::new(B);
let mut b1 = SharedBox::from_box(b1);
let mut b2 = SharedBox::from_box(b2);
let r1 = b1.borrow();
let r2 = b2.borrow();
assert_eq!(r1.get().f(), 0);
assert_eq!(r2.get().f(), 1);
let r2 = b1.try_return(r2).unwrap_err();
let r1 = b2.try_return(r1).unwrap_err();
assert!(b1.try_return(r1).is_ok());
assert!(b2.try_return(r2).is_ok());
}
#[test]
fn zst_slice() {
let b1: Box<[()]> = Box::new([(); 1]);
let b2: Box<[()]> = Box::new([(); 2]);
let mut b1 = SharedBox::from_box(b1);
let mut b2 = SharedBox::from_box(b2);
let r1 = b1.borrow();
let r2 = b2.borrow();
let r2 = b1.try_return(r2).unwrap_err();
let r1 = b2.try_return(r1).unwrap_err();
assert!(b1.try_return(r1).is_ok());
assert!(b2.try_return(r2).is_ok());
}
}