#![doc = include_str!("../README.md")]
#![cfg_attr(feature = "nightly", feature(generic_associated_types))]
use std::cell::Cell;
use std::marker::PhantomData;
use std::ops::Deref;
use std::ptr::null_mut;
use std::sync::Arc;
use parking_lot::{Mutex, MutexGuard};
type Inner = Arc<Mutex<*mut u8>>;
pub struct Lifelink<C: Ctor> {
inner: Inner,
_marker: PhantomData<C::Ty<'static>>,
}
unsafe impl<C: SendCtor> Send for Lifelink<C> {}
unsafe impl<C: SendCtor> Sync for Lifelink<C> {}
#[macro_export]
macro_rules! lifelink {
($lifelink:ident : $ty:ty = $thing:expr) => {
let (mut $lifelink, _deathtouch) = unsafe { $crate::Lifelink::<$ty>::new($thing) };
};
}
impl<C: Ctor> Lifelink<C> {
#[allow(clippy::needless_lifetimes)] pub unsafe fn new<'a>(thing: C::Ty<'a>) -> (Lifelink<C>, Deathtouch<'a, C>) {
let thing = Box::new(thing);
let ptr = Box::into_raw(thing) as *mut u8;
let inner = Arc::new(Mutex::new(ptr));
let lifelink = Lifelink {
inner: Arc::clone(&inner),
_marker: PhantomData,
};
let deathtouch = Deathtouch {
inner,
_marker: PhantomData,
};
(lifelink, deathtouch)
}
pub fn get(&mut self) -> Option<Guard<'_, C>> {
let lock = self.inner.lock();
if lock.is_null() {
None
} else {
Some(Guard {
lock,
_marker: PhantomData,
})
}
}
}
pub struct Guard<'a, C: Ctor> {
lock: MutexGuard<'a, *mut u8>,
_marker: PhantomData<C::Ty<'static>>,
}
unsafe impl<'a, C: SyncCtor> Send for Guard<'a, C> {}
unsafe impl<'a, C: SyncCtor> Sync for Guard<'a, C> {}
impl<'a, C> Deref for Guard<'a, C>
where
C: Ctor + Cov,
{
type Target = C::Ty<'a>;
fn deref(&self) -> &Self::Target {
let ptr: *mut u8 = *self.lock;
unsafe { &*(ptr as *const C::Ty<'a>) }
}
}
pub struct Deathtouch<'a, C: Ctor> {
inner: Inner,
_marker: PhantomData<Cell<&'a C::Ty<'a>>>,
}
unsafe impl<'a, C: SendCtor> Send for Deathtouch<'a, C> {}
impl<'a, C: Ctor> Deathtouch<'a, C> {
fn extract(&mut self) -> Option<C::Ty<'a>> {
let mut lock = self.inner.lock();
let ptr: *mut u8 = std::mem::replace(&mut *lock, null_mut());
if ptr.is_null() {
None
} else {
unsafe { Some(*Box::from_raw(ptr as *mut C::Ty<'a>)) }
}
}
pub fn unwrap(mut self) -> C::Ty<'a> {
self.extract().unwrap()
}
}
impl<'a, C: Ctor> Drop for Deathtouch<'a, C> {
fn drop(&mut self) {
self.extract();
}
}
pub trait Ctor {
type Ty<'a>;
}
pub unsafe trait Cov: Ctor {
fn cov<'r, 'a, 'b>(r: &'r Self::Ty<'a>) -> &'r Self::Ty<'b>
where
'a: 'b;
}
#[macro_export]
macro_rules! cov {
(<$($tv:ident $(: $bound:tt $(+ $bounds:tt)*)?),*> $thing:ty) => {
unsafe impl<$($tv $(: $bound $(+ $bounds)*)?),*> $crate::Cov for $thing {
fn cov<'r, 'a, 'b>(r: &'r <Self as $crate::Ctor>::Ty<'a>) -> &'r <Self as $crate::Ctor>::Ty<'b>
where
'a: 'b,
{
r
}
}
};
($thing:ty) => {
$crate::cov!(<> $thing);
};
}
pub trait SendCtor: Ctor {}
impl<C> SendCtor for C
where
C: Ctor,
for<'a> C::Ty<'a>: Send,
{
}
pub trait SyncCtor: Ctor {}
impl<C> SyncCtor for C
where
C: Ctor,
for<'a> C::Ty<'a>: Sync,
{
}
pub struct RefCtor<T> {
_marker: PhantomData<T>,
}
impl<T: 'static> Ctor for RefCtor<T> {
type Ty<'a> = &'a T;
}
cov!(<T: 'static> RefCtor<T>);
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
#[test]
fn local() {
let answer = AtomicUsize::new(0);
let mut leaked;
{
lifelink!(lifelink: RefCtor<AtomicUsize> = &answer);
assert_eq!(
Some(0),
lifelink.get().map(|foo| foo.load(Ordering::Relaxed))
);
leaked = lifelink;
}
assert!(leaked.get().is_none());
}
#[test]
fn sync() {
use std::sync::mpsc::channel;
use std::thread::spawn;
let answer = AtomicUsize::new(0);
lifelink!(lifelink: RefCtor<AtomicUsize> = &answer);
let (send, recv) = channel();
spawn(move || {
let guard = lifelink.get().unwrap();
assert_eq!(0, guard.load(Ordering::Relaxed));
guard.store(42, Ordering::Release);
send.send(()).unwrap();
});
recv.recv_timeout(Duration::from_millis(20)).unwrap();
assert_eq!(42, answer.load(Ordering::Acquire));
}
}