use {
crate::{ANCHOR_DROPPED, ANCHOR_POISONED, ANCHOR_STILL_IN_USE},
log::error,
std::{
borrow::Borrow,
cell::{Ref, RefCell, RefMut},
fmt::Debug,
marker::PhantomData,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
panic::{RefUnwindSafe, UnwindSafe},
ptr::NonNull,
rc::{Rc, Weak},
sync::Mutex, thread,
},
wyz::pipe::*,
};
#[derive(Debug)]
struct Poisonable<T> {
pointer: T,
poisoned: bool,
}
#[derive(Debug)]
#[repr(transparent)]
pub struct Anchor<'a, T: ?Sized> {
reference: ManuallyDrop<Rc<NonNull<T>>>,
_phantom: PhantomData<&'a T>,
}
#[derive(Debug)]
#[repr(transparent)]
pub struct RwAnchor<'a, T: ?Sized> {
reference: ManuallyDrop<Rc<RefCell<Poisonable<NonNull<T>>>>>,
_phantom: PhantomData<&'a mut T>,
}
impl<'a, T: ?Sized> Anchor<'a, T> {
pub fn new(reference: &'a T) -> Anchor<'a, T> {
Self {
reference: ManuallyDrop::new(Rc::new(reference.into())),
_phantom: PhantomData,
}
}
#[inline]
pub fn portal(&self) -> Portal<T> {
self.reference.pipe_deref(Rc::clone).pipe(Portal)
}
#[inline]
pub fn weak_portal(&self) -> WeakPortal<T> {
Portal::downgrade(&self.portal())
}
}
impl<'a, T: ?Sized> RwAnchor<'a, T> {
pub fn new(reference: &'a mut T) -> Self {
Self {
reference: ManuallyDrop::new(Rc::new(RefCell::new(Poisonable {
pointer: reference.into(),
poisoned: false,
}))),
_phantom: PhantomData,
}
}
#[inline]
pub fn portal(&self) -> RwPortal<T> {
self.reference.pipe_deref(Rc::clone).pipe(RwPortal)
}
#[inline]
pub fn weak_portal(&self) -> WeakRwPortal<T> {
self.portal().downgrade()
}
}
impl<'a, T: ?Sized> Drop for Anchor<'a, T> {
fn drop(&mut self) {
unsafe {
ManuallyDrop::take(&mut self.reference)
}
.pipe(Rc::try_unwrap)
.unwrap_or_else(|_pointer| {
error!("!Send `Anchor` dropped while at least one Portal still exists. Deadlocking thread to prevent UB.");
let deadlock_mutex = Mutex::new(());
let _deadlock_guard = deadlock_mutex.lock().unwrap();
let _never = deadlock_mutex.lock();
unreachable!()
});
}
}
impl<'a, T: ?Sized> Drop for RwAnchor<'a, T> {
fn drop(&mut self) {
unsafe {
ManuallyDrop::take(&mut self.reference)
}
.pipe(Rc::try_unwrap)
.unwrap_or_else(|reference| {
reference
.try_borrow_mut()
.unwrap_or_else(|_| {
error!("!Send `RwAnchor` dropped while borrowed from. Deadlocking thread to prevent UB.");
let deadlock_mutex = Mutex::new(());
let _deadlock_guard = deadlock_mutex.lock().unwrap();
let _never = deadlock_mutex.lock();
unreachable!()
})
.poisoned = true;
panic!(ANCHOR_STILL_IN_USE)
})
.into_inner() .poisoned
.pipe(|poisoned| {
if poisoned {
panic!(ANCHOR_POISONED)
}
})
}
}
impl<'a, T: ?Sized> UnwindSafe for Anchor<'a, T> where T: RefUnwindSafe {}
impl<'a, T: ?Sized> UnwindSafe for RwAnchor<'a, T> where T: RefUnwindSafe {}
#[derive(Debug)]
#[must_use]
#[repr(transparent)]
pub struct Portal<T: ?Sized>(Rc<NonNull<T>>);
#[derive(Debug)]
#[must_use]
#[repr(transparent)]
pub struct RwPortal<T: ?Sized>(Rc<RefCell<Poisonable<NonNull<T>>>>);
impl<T: ?Sized> Portal<T> {
#[inline]
pub fn downgrade(portal: &Self) -> WeakPortal<T> {
Rc::downgrade(&portal.0).pipe(WeakPortal)
}
}
impl<T: ?Sized> Deref for Portal<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
let pointer = self.0.deref();
unsafe {
pointer.as_ref()
}
}
}
impl<T: ?Sized> Borrow<T> for Portal<T> {
#[inline]
fn borrow(&self) -> &T {
&*self
}
}
impl<T: ?Sized> RwPortal<T> {
#[inline]
pub fn downgrade(&self) -> WeakRwPortal<T> {
Rc::downgrade(&self.0).pipe(WeakRwPortal)
}
#[inline]
pub fn borrow<'a>(&'a self) -> impl Deref<Target = T> + 'a {
let guard = self.0.as_ref().borrow();
if guard.poisoned {
panic!(ANCHOR_POISONED)
}
PortalRef(guard)
}
#[inline]
pub fn borrow_mut<'a>(&'a self) -> impl DerefMut<Target = T> + 'a {
let guard = self.0.as_ref().borrow_mut();
if guard.poisoned {
panic!(ANCHOR_POISONED)
}
PortalRefMut(guard)
}
}
impl<T: ?Sized> Clone for Portal<T> {
#[inline]
fn clone(&self) -> Self {
self.0.pipe_ref(Rc::clone).pipe(Self)
}
}
impl<T: ?Sized> Clone for RwPortal<T> {
#[inline]
fn clone(&self) -> Self {
self.0.pipe_ref(Rc::clone).pipe(Self)
}
}
impl<T: ?Sized> RefUnwindSafe for RwPortal<T> where T: RefUnwindSafe {}
impl<T: ?Sized> UnwindSafe for RwPortal<T> where T: RefUnwindSafe {}
#[derive(Debug)]
#[must_use]
#[repr(transparent)]
pub struct WeakPortal<T: ?Sized>(Weak<NonNull<T>>);
#[derive(Debug)]
#[must_use]
#[repr(transparent)]
pub struct WeakRwPortal<T: ?Sized>(Weak<RefCell<Poisonable<NonNull<T>>>>);
impl<T: ?Sized> WeakPortal<T> {
#[inline]
pub fn try_upgrade(&self) -> Option<Portal<T>> {
self.0.upgrade().map(Portal)
}
#[inline]
pub fn upgrade(&self) -> Portal<T> {
self.try_upgrade().expect(ANCHOR_DROPPED)
}
}
impl<T: ?Sized> WeakRwPortal<T> {
#[inline]
pub fn try_upgrade(&self) -> Option<RwPortal<T>> {
self.0.upgrade().map(RwPortal)
}
#[inline]
pub fn upgrade(&self) -> RwPortal<T> {
self.try_upgrade().expect(ANCHOR_DROPPED)
}
}
impl<T: ?Sized> Clone for WeakPortal<T> {
#[inline]
fn clone(&self) -> Self {
self.0.pipe_ref(Weak::clone).pipe(Self)
}
}
impl<T: ?Sized> Clone for WeakRwPortal<T> {
#[inline]
fn clone(&self) -> Self {
self.0.pipe_ref(Weak::clone).pipe(Self)
}
}
#[repr(transparent)]
struct PortalRef<'a, T: 'a + ?Sized>(Ref<'a, Poisonable<NonNull<T>>>);
#[repr(transparent)]
struct PortalRefMut<'a, T: 'a + ?Sized>(RefMut<'a, Poisonable<NonNull<T>>>);
impl<'a, T: ?Sized> Deref for PortalRef<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
let pointer = &self.0.deref().pointer;
unsafe {
pointer.as_ref()
}
}
}
impl<'a, T: ?Sized> Deref for PortalRefMut<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
let pointer = &self.0.deref().pointer;
unsafe {
pointer.as_ref()
}
}
}
impl<'a, T: ?Sized> DerefMut for PortalRefMut<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
let pointer = &mut self.0.deref_mut().pointer;
unsafe {
pointer.as_mut()
}
}
}
impl<'a, T: ?Sized> Drop for PortalRefMut<'a, T> {
#[inline]
fn drop(&mut self) {
if thread::panicking() {
self.0.poisoned = true;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn _auto_trait_assertions() {
use {assert_impl::assert_impl, core::any::Any};
assert_impl!(
!Send: Anchor<'_, ()>,
RwAnchor<'_, ()>,
Portal<()>,
RwPortal<()>,
PortalRef<'_, ()>,
PortalRefMut<'_, ()>,
);
assert_impl!(
!Sync: Anchor<'_, ()>,
RwAnchor<'_, ()>,
Portal<()>,
RwPortal<()>,
PortalRef<'_, ()>,
PortalRefMut<'_, ()>,
);
assert_impl!(
!UnwindSafe: Anchor<'_, dyn UnwindSafe>,
RwAnchor<'_, dyn UnwindSafe>,
Portal<dyn UnwindSafe>,
RwPortal<dyn UnwindSafe>,
);
assert_impl!(
UnwindSafe: Anchor<'_, dyn RefUnwindSafe>,
RwAnchor<'_, dyn RefUnwindSafe>,
Portal<dyn RefUnwindSafe>,
RwPortal<dyn RefUnwindSafe>,
);
assert_impl!(!UnwindSafe: PortalRef<'_, ()>, PortalRefMut<'_, ()>);
assert_impl!(!RefUnwindSafe: RwPortal<dyn UnwindSafe>);
assert_impl!(RefUnwindSafe: RwPortal<dyn RefUnwindSafe>);
assert_impl!(
!RefUnwindSafe: Anchor<'_, ()>,
RwAnchor<'_, ()>,
Portal<()>,
PortalRef<'_, ()>,
PortalRefMut<'_, ()>,
);
assert_impl!(
Unpin: Anchor<'_, dyn Any>,
RwAnchor<'_, dyn Any>,
Portal<dyn Any>,
RwPortal<dyn Any>,
PortalRef<'_, dyn Any>,
PortalRefMut<'_, dyn Any>,
)
}
fn _impl_trait_assertions() {
use {assert_impl::assert_impl, core::any::Any};
assert_impl!(
Clone: Portal<dyn Any>,
RwPortal<dyn Any>,
WeakPortal<dyn Any>,
WeakRwPortal<dyn Any>,
);
assert_impl!(Deref<Target = dyn Any>: Portal<dyn Any>);
assert_impl!(Borrow<dyn Any>: Portal<dyn Any>);
}
}