use crate::{lich::Lich, shroud::Shroud};
use core::{
borrow::Borrow,
marker::PhantomPinned,
mem::{ManuallyDrop, forget},
ops::Deref,
pin::Pin,
ptr::{self, NonNull, drop_in_place, read},
sync::atomic::{AtomicU32, Ordering},
};
#[derive(Debug)]
pub struct Soul<T: ?Sized> {
_marker: PhantomPinned,
count: AtomicU32,
value: T,
}
impl<T> Soul<T> {
pub const fn new(value: T) -> Self {
Self {
value,
count: AtomicU32::new(0),
_marker: PhantomPinned,
}
}
pub fn consume(self) -> T {
let mut soul = ManuallyDrop::new(self);
unsafe { drop_in_place(&mut soul.count) };
unsafe { read(&soul.value) }
}
}
impl<T: ?Sized> Soul<T> {
pub fn bind<S: Shroud<T> + ?Sized>(self: Pin<&Self>) -> Lich<S> {
self.count.fetch_add(1, Ordering::Relaxed);
Lich {
count: self.count_ptr(),
value: S::shroud(self.value_ptr()),
}
}
pub fn is_bound<S: ?Sized>(&self, lich: &Lich<S>) -> bool {
ptr::eq(&self.count, lich.count.as_ptr())
}
pub fn bindings(&self) -> usize {
self.count
.load(Ordering::Relaxed)
.wrapping_add(1)
.saturating_sub(1) as _
}
pub fn redeem<S: ?Sized>(&self, lich: Lich<S>) -> Result<usize, Lich<S>> {
if self.is_bound(&lich) {
forget(lich);
Ok(self.count.fetch_sub(1, Ordering::Relaxed) as _)
} else {
Err(lich)
}
}
pub fn sever<S: Deref<Target = Self>>(this: Pin<S>) -> S {
if sever::<true>(&this.count) {
unsafe { Self::unpin(this) }
} else {
panic!("sever failed possibly due to unwinding")
}
}
pub fn try_sever<S: Deref<Target = Self>>(this: Pin<S>) -> Result<S, Pin<S>> {
if sever::<false>(&this.count) {
Ok(unsafe { Self::unpin(this) })
} else {
Err(this)
}
}
unsafe fn unpin<S: Deref<Target = Self>>(this: Pin<S>) -> S {
debug_assert_eq!(this.bindings(), 0);
unsafe { Pin::into_inner_unchecked(this) }
}
fn value_ptr(self: Pin<&Self>) -> NonNull<T> {
unsafe { NonNull::new_unchecked(&self.value as *const _ as _) }
}
fn count_ptr(self: Pin<&Self>) -> NonNull<AtomicU32> {
unsafe { NonNull::new_unchecked(&self.count as *const _ as _) }
}
}
impl<T: ?Sized> Deref for Soul<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: ?Sized> AsRef<T> for Soul<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl<T: ?Sized> Borrow<T> for Soul<T> {
fn borrow(&self) -> &T {
&self.value
}
}
impl<T: ?Sized> Drop for Soul<T> {
fn drop(&mut self) {
sever::<true>(&self.count);
}
}
fn sever<const FORCE: bool>(count: &AtomicU32) -> bool {
loop {
match count.compare_exchange(0, u32::MAX, Ordering::Acquire, Ordering::Relaxed) {
Ok(0 | u32::MAX) | Err(u32::MAX) => break true,
Ok(value) | Err(value) if FORCE => atomic_wait::wait(count, value),
Ok(_) | Err(_) => break false,
}
}
}