use core::{
cell::UnsafeCell,
fmt,
marker::PhantomData,
ops::{Deref, DerefMut},
ptr::NonNull,
sync::atomic::{AtomicUsize, Ordering},
};
pub struct ArfCell<T> {
state: AtomicUsize,
item: UnsafeCell<T>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BorrowError {
mutable: bool,
}
unsafe impl<T> Sync for ArfCell<T> where T: Send {}
pub struct MutArfGuard<'a, T> {
cell: NonNull<ArfCell<T>>,
plt: PhantomData<&'a mut T>,
}
impl<'a, T> Deref for MutArfGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.cell.as_ref().item.get() }
}
}
impl<'a, T> DerefMut for MutArfGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.cell.as_mut().item.get_mut() }
}
}
impl<'a, T> Drop for MutArfGuard<'a, T> {
fn drop(&mut self) {
unsafe {
self.cell.as_ref().state.store(0, Ordering::Release);
}
}
}
pub struct ArfGuard<'a, T> {
cell: NonNull<ArfCell<T>>,
plt: PhantomData<&'a mut T>,
}
impl<'a, T> Deref for ArfGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.cell.as_ref().item.get() }
}
}
impl<'a, T> Drop for ArfGuard<'a, T> {
fn drop(&mut self) {
unsafe {
let x = self.cell.as_ref().state.fetch_sub(1, Ordering::AcqRel);
debug_assert!(x != 0, "Underflow on refcnt release!");
}
}
}
impl<T> ArfCell<T> {
const MUTLOCK: usize = (usize::MAX / 2) + 1;
pub const fn new(item: T) -> Self {
ArfCell {
state: AtomicUsize::new(0),
item: UnsafeCell::new(item),
}
}
pub fn borrow_mut(&self) -> Result<MutArfGuard<'_, T>, BorrowError> {
self.state
.compare_exchange(
0,
Self::MUTLOCK,
Ordering::SeqCst,
Ordering::SeqCst,
)
.map_err(|_| BorrowError { mutable: false })?;
Ok(MutArfGuard {
cell: unsafe { NonNull::new_unchecked(self as *const Self as *mut Self) },
plt: PhantomData,
})
}
pub fn borrow(&self) -> Result<ArfGuard<'_, T>, BorrowError> {
if self.state.load(Ordering::Acquire) >= Self::MUTLOCK {
return Err(BorrowError { mutable: true });
}
let old = self.state.fetch_add(1, Ordering::AcqRel);
if old >= Self::MUTLOCK {
return Err(BorrowError { mutable: true });
}
Ok(ArfGuard {
cell: unsafe { NonNull::new_unchecked(self as *const Self as *mut Self) },
plt: PhantomData,
})
}
}
impl fmt::Display for BorrowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.mutable {
write!(f, "already mutably borrowed")
} else {
write!(f, "already borrowed")
}
}
}