use super::LeasedMut;
use core::{
any::{Any, TypeId},
borrow::Borrow,
cell::Cell,
fmt,
ops::{Deref, DerefMut},
panic::{Location, RefUnwindSafe, UnwindSafe},
pin::Pin,
ptr::NonNull,
sync::atomic::{AtomicUsize, Ordering},
};
pub trait AsIrc<T: ?Sized + IntrusivelyCounted> {
fn as_irc(&self) -> Irc<T>;
}
pub unsafe trait IntrusivelyCounted {
fn irc_box(&self) -> &IrcBox<dyn IrcBoxed>;
}
pub unsafe trait IrcBoxed {
fn ref_count(&self) -> usize;
fn acquire(&self, _: Private);
fn release(&self, _: Private);
#[inline(always)]
fn reclaim(&self, _: Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
None
}
}
pub struct Irc<T: ?Sized + IntrusivelyCounted>(NonNull<T>);
impl<T: ?Sized + IntrusivelyCounted> Irc<T> {
pub fn new(value: Pin<LeasedMut<'_, T>>) -> Self {
value.irc_box().count.acquire(Private(()));
Irc(NonNull::from(&mut **unsafe {
Pin::into_inner_unchecked(value)
}))
}
pub unsafe fn new_unchecked(value: Pin<&mut T>) -> Self {
value.irc_box().acquire(Private(()));
Irc(NonNull::from(unsafe { Pin::into_inner_unchecked(value) }))
}
pub fn get_pin(&self) -> Pin<&T> {
unsafe { Pin::new_unchecked(self.0.as_ref()) }
}
pub fn get_pin_mut(&mut self) -> Option<Pin<&mut T>> {
(self.irc_box().ref_count() == 1).then(|| unsafe { Pin::new_unchecked(self.0.as_mut()) })
}
pub fn as_raw(this: &Self) -> NonNull<T> {
this.0
}
pub fn into_raw(this: Self) -> NonNull<T> {
let inner = this.0;
core::mem::forget(this);
inner
}
pub unsafe fn from_raw(inner: NonNull<T>) -> Self {
Irc(inner)
}
pub fn map<F, R>(this: Self, f: F) -> Irc<R>
where
F: FnOnce(&T) -> &R,
R: ?Sized + IntrusivelyCounted,
{
let inner = this.irc_box();
let value = f(&*this);
assert!(
core::ptr::addr_eq(inner, value.irc_box()),
"expected the mapping to yield an Irc with the same IrcBox"
);
let res = Irc(with_provenance(NonNull::from(value), this.0));
core::mem::forget(this);
res
}
pub fn downcast<V>(self) -> Result<Irc<V>, Irc<T>>
where
T: Any,
V: 'static + IntrusivelyCounted,
{
if (*self).type_id() == TypeId::of::<V>() {
let res = Irc(self.0.cast::<V>());
core::mem::forget(self);
Ok(res)
} else {
Err(self)
}
}
}
impl<T: ?Sized + IntrusivelyCounted> Deref for Irc<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl<T: ?Sized + IntrusivelyCounted> Clone for Irc<T> {
fn clone(&self) -> Self {
self.irc_box().acquire(Private(()));
Irc(self.0)
}
}
impl<T: ?Sized + IntrusivelyCounted> Borrow<T> for Irc<T> {
fn borrow(&self) -> &T {
self
}
}
impl<T: ?Sized + IntrusivelyCounted> Drop for Irc<T> {
fn drop(&mut self) {
let irc_box = self.irc_box();
irc_box.release(Private(()));
if irc_box.ref_count() == 0 {
let Some(reclaim) = irc_box.reclaim(Private(())) else {
return;
};
let irc_box = with_provenance(NonNull::from(&irc_box.count), self.0);
reclaim(irc_box);
}
}
}
impl<T: ?Sized + IntrusivelyCounted> Unpin for Irc<T> {}
impl<T: ?Sized + IntrusivelyCounted + fmt::Debug> fmt::Debug for Irc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { self.0.as_ref() }.fmt(f)
}
}
impl<T: ?Sized + IntrusivelyCounted + fmt::Display> fmt::Display for Irc<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe { self.0.as_ref() }.fmt(f)
}
}
unsafe impl<T: Sync + ?Sized + IntrusivelyCounted> Send for Irc<T> {}
unsafe impl<T: Sync + ?Sized + IntrusivelyCounted> Sync for Irc<T> {}
impl<T: RefUnwindSafe + ?Sized + IntrusivelyCounted> UnwindSafe for Irc<T> {}
pub struct Private(());
fn with_provenance<S: ?Sized, T: ?Sized>(
mut target: NonNull<T>,
provenance: NonNull<S>,
) -> NonNull<T> {
let target_thin_ptr = provenance.cast::<u8>().with_addr(target.addr());
let ptr_to_fat_ptr = NonNull::from(&mut target).cast::<NonNull<u8>>();
unsafe {
ptr_to_fat_ptr.write(target_thin_ptr);
}
target
}
#[derive(Debug)]
pub struct IrcBox<T: ?Sized + IrcBoxed = Cell<usize>> {
loc: &'static Location<'static>,
count: T,
}
impl<T: IrcBoxed> IrcBox<T> {
#[track_caller]
pub const fn new(count: T) -> Self {
IrcBox::with_location(count, Location::caller())
}
pub const fn with_location(count: T, loc: &'static Location<'static>) -> Self {
IrcBox { loc, count }
}
pub const fn location(this: &Self) -> &'static Location<'static> {
this.loc
}
}
impl<T: IrcBoxed + Default> Default for IrcBox<T> {
#[track_caller]
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: ?Sized + IrcBoxed> Deref for IrcBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.count
}
}
impl<T: ?Sized + IrcBoxed> DerefMut for IrcBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.count
}
}
impl<T: ?Sized + IrcBoxed> Drop for IrcBox<T> {
fn drop(&mut self) {
extern "C" fn abort(loc: &Location<'static>, n: usize) -> ! {
panic!(
"dropping the value created at '{}' leaves {} reference(s) dangling",
loc, n,
)
}
match self.count.ref_count() {
0 => {}
n => abort(self.loc, n),
}
}
}
unsafe impl IrcBoxed for Cell<usize> {
#[inline(always)]
fn ref_count(&self) -> usize {
self.get()
}
#[inline(always)]
fn acquire(&self, _: Private) {
self.set(self.get() + 1);
}
#[inline(always)]
fn release(&self, _: Private) {
self.set(self.get() - 1);
}
}
unsafe impl IrcBoxed for AtomicUsize {
#[inline(always)]
fn ref_count(&self) -> usize {
self.load(Ordering::Relaxed)
}
#[inline(always)]
fn acquire(&self, _: Private) {
self.fetch_add(1, Ordering::Relaxed);
}
#[inline(always)]
fn release(&self, _: Private) {
self.fetch_sub(1, Ordering::Relaxed);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ptr::Lease;
use core::pin::pin;
#[test]
fn provenance_transfer() {
#[derive(Default)]
struct Outer {
inner: Inner,
}
#[derive(Default)]
struct Inner {
irc_box: IrcBox<InnerBox>,
}
#[derive(Default)]
struct InnerBox {
rc: Cell<usize>,
term: bool,
}
unsafe impl IrcBoxed for InnerBox {
fn ref_count(&self) -> usize {
self.rc.get()
}
fn acquire(&self, _: Private) {
self.rc.set(self.rc.get() + 1);
}
fn release(&self, _: Private) {
self.rc.set(self.rc.get() - 1);
}
fn reclaim(&self, _: Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
Some(|this| unsafe {
this.cast::<Self>().as_mut().term = true;
})
}
}
unsafe impl IntrusivelyCounted for Outer {
fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
&self.inner.irc_box
}
}
unsafe impl IntrusivelyCounted for Inner {
fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
&self.irc_box
}
}
let outer: Pin<&mut Lease<'_, Outer>> = pin!(Lease::new(Outer::default()));
let irc1: Irc<Outer> = Irc::new(outer);
let irc2: Irc<Inner> = Irc::map(irc1, |outer| &outer.inner);
assert_eq!(irc2.irc_box.ref_count(), 1);
drop(irc2);
}
#[test]
fn self_reference() {
#[derive(Default)]
struct Outer {
inner: Inner,
done: bool,
}
#[derive(Default)]
struct Inner {
irc_box: IrcBox<InnerBox>,
}
#[derive(Default)]
struct InnerBox {
rc: Cell<usize>,
outer_ref: Cell<Option<NonNull<Outer>>>,
}
unsafe impl IrcBoxed for InnerBox {
fn ref_count(&self) -> usize {
self.rc.get()
}
fn acquire(&self, _: Private) {
self.rc.set(self.rc.get() + 1);
}
fn release(&self, _: Private) {
self.rc.set(self.rc.get() - 1);
}
fn reclaim(&self, _: Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
Some(|this| unsafe {
let mut outer_ref = this
.cast::<Self>()
.as_ref()
.outer_ref
.get()
.expect("self-reference initialized");
outer_ref.as_mut().done = true;
})
}
}
unsafe impl IntrusivelyCounted for Outer {
fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
&self.inner.irc_box
}
}
unsafe impl IntrusivelyCounted for Inner {
fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
&self.irc_box
}
}
let outer = pin!(Lease::new(Outer::default()));
let irc1 = Irc::new(outer);
let outer_ref = Irc::as_raw(&irc1);
irc1.inner.irc_box.outer_ref.set(Some(outer_ref));
let irc2 = Irc::map(irc1, |inner| &inner.inner);
drop(irc2);
}
#[test]
fn self_reference2() {
#[derive(Default)]
struct Outer {
inner: Inner,
done: bool,
}
#[derive(Default)]
struct Inner {
irc_box: IrcBox<InnerBox>,
}
#[derive(Default)]
struct InnerBox {
ref_count: Cell<usize>,
outer_ref: Cell<Option<NonNull<Outer>>>,
}
unsafe impl IrcBoxed for InnerBox {
fn ref_count(&self) -> usize {
self.ref_count.get()
}
fn acquire(&self, _: Private) {
self.ref_count.set(self.ref_count.get() + 1);
}
fn release(&self, _: Private) {
self.ref_count.set(self.ref_count.get() - 1);
}
fn reclaim(&self, _: Private) -> Option<fn(NonNull<dyn IrcBoxed>)> {
Some(|this| unsafe {
let mut outer_ref = this
.cast::<Self>()
.as_ref()
.outer_ref
.get()
.expect("self-reference initialized");
outer_ref.as_mut().done = true;
})
}
}
unsafe impl IntrusivelyCounted for Outer {
fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
&self.inner.irc_box
}
}
unsafe impl IntrusivelyCounted for Inner {
fn irc_box(&self) -> &IrcBox<dyn IrcBoxed> {
&self.irc_box
}
}
let mut outer = pin!(Lease::new(Outer::default()));
let outer_ref = NonNull::from(unsafe { outer.as_mut().project().get_unchecked_mut() });
let irc1 = Irc::new(outer);
let irc2 = Irc::map(irc1, |inner| &inner.inner);
let outer_ref = with_provenance(outer_ref, Irc::as_raw(&irc2));
irc2.irc_box.outer_ref.set(Some(outer_ref));
drop(irc2);
}
}