#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
extern crate alloc;
#[cfg(any(doc, test))]
extern crate std;
#[cfg(test)]
mod tests;
use alloc::boxed::Box;
use core::sync::atomic;
use core::{fmt, hint, marker, mem, ops, ptr};
use crate::private::BilockLike as _;
pub struct Bilock<T> {
ptr: ptr::NonNull<Inner<T>>,
}
pub struct Guard<'a, T> {
ptr: ptr::NonNull<Inner<T>>,
_bilock: marker::PhantomData<&'a mut Bilock<T>>,
}
pub struct OwnedGuard<T> {
ptr: ptr::NonNull<Inner<T>>,
}
struct Inner<T> {
value: T,
state: atomic::AtomicU8,
}
pub trait BilockLike: private::BilockLike {
#[inline]
fn ptr_eq(left: &Self, right: &impl BilockLike) -> bool {
ptr::addr_eq(left.state(), right.state())
}
#[inline]
fn other_side_alive(&self) -> bool {
self.state().load(atomic::Ordering::Acquire) & ALIVE_FLAG == ALIVE_FLAG
}
}
impl<T> BilockLike for Bilock<T> {}
impl<T> BilockLike for Guard<'_, T> {}
impl<T> BilockLike for OwnedGuard<T> {}
impl<T: BilockLike> BilockLike for &T {}
unsafe impl<T: Send + Sync> Send for Bilock<T> {}
unsafe impl<T: Send + Sync> Sync for Bilock<T> {}
unsafe impl<T: Send + Sync> Send for Guard<'_, T> {}
unsafe impl<T: Send + Sync> Sync for Guard<'_, T> {}
unsafe impl<T: Send + Sync> Send for OwnedGuard<T> {}
unsafe impl<T: Send + Sync> Sync for OwnedGuard<T> {}
impl<T> fmt::Debug for Bilock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_ptr(f, "Bilock", self.value())
}
}
impl<T> fmt::Debug for Guard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_ptr(f, "Guard", self.value())
}
}
impl<T> fmt::Debug for OwnedGuard<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_ptr(f, "OwnedGuard", self.value())
}
}
#[inline]
fn debug_ptr(f: &mut fmt::Formatter<'_>, name: &str, ptr: *const ()) -> Result<(), fmt::Error> {
f.debug_struct(name).field("ptr", &ptr).finish()
}
impl<T> Bilock<T> {
#[inline]
pub fn new(value: T) -> (Self, Self) {
let ptr = Inner::new(value, ALIVE_FLAG | UNLOCKED_FLAG);
(Self { ptr }, Self { ptr })
}
#[inline]
pub fn new_locked(value: T) -> (OwnedGuard<T>, Bilock<T>) {
let ptr = Inner::new(value, ALIVE_FLAG);
(OwnedGuard { ptr }, Bilock { ptr })
}
#[inline]
pub fn new_unpaired(value: T) -> Self {
Self {
ptr: Inner::new(value, UNLOCKED_FLAG),
}
}
#[inline]
pub fn owned_lock(mut self) -> OwnedGuard<T> {
let guard = unsafe { Guard::into_owned(self.lock()) };
mem::forget(self);
guard
}
pub fn lock(&mut self) -> Guard<'_, T> {
let mut this = self;
loop {
this = match this.do_try_lock() {
Ok(guard) => return guard,
Err(this) => this,
};
hint::spin_loop();
}
}
#[inline]
pub fn try_owned_lock(mut self) -> Result<OwnedGuard<T>, Self> {
let Some(guard) = self.try_lock() else {
return Err(self);
};
let guard = unsafe { Guard::into_owned(guard) };
mem::forget(self);
Ok(guard)
}
#[inline]
pub fn try_lock(&mut self) -> Option<Guard<'_, T>> {
self.do_try_lock().ok()
}
fn do_try_lock(&mut self) -> Result<Guard<'_, T>, &mut Self> {
let mut old_state = self.state().load(atomic::Ordering::Acquire);
loop {
if old_state & UNLOCKED_FLAG != UNLOCKED_FLAG {
return Err(self);
} else if let Err(new_state) = self.state().compare_exchange_weak(
old_state,
old_state & !UNLOCKED_FLAG,
atomic::Ordering::Acquire,
atomic::Ordering::Relaxed,
) {
old_state = new_state;
hint::spin_loop();
} else {
return Ok(Guard {
ptr: self.ptr,
_bilock: marker::PhantomData,
});
}
}
}
#[inline]
pub fn join(guard: OwnedGuard<T>, other: Self) -> Result<T, (OwnedGuard<T>, Self)> {
if guard.ptr == other.ptr {
Ok(unsafe { Self::join_unchecked(guard, other) })
} else {
Err((guard, other))
}
}
#[inline]
pub unsafe fn join_unchecked(guard: OwnedGuard<T>, other: Self) -> T {
drop(other);
unsafe { Self::into_inner_unchecked(guard) }
}
#[inline]
pub fn into_inner(guard: OwnedGuard<T>) -> Result<T, OwnedGuard<T>> {
if guard.state().load(atomic::Ordering::Acquire) & ALIVE_FLAG != ALIVE_FLAG {
Ok(unsafe { Self::into_inner_unchecked(guard) })
} else {
Err(guard)
}
}
#[inline]
pub unsafe fn into_inner_unchecked(guard: OwnedGuard<T>) -> T {
let inner = unsafe { Box::from_raw(guard.ptr.as_ptr()) };
mem::forget(guard);
inner.value
}
#[inline]
pub fn revive(guard: &mut OwnedGuard<T>) -> Option<Self> {
let old_state = guard.state().fetch_or(ALIVE_FLAG, atomic::Ordering::AcqRel);
if old_state & ALIVE_FLAG != ALIVE_FLAG {
Some(Self { ptr: guard.ptr })
} else {
None
}
}
#[inline]
pub unsafe fn revive_unchecked(guard: &mut OwnedGuard<T>) -> Self {
let _: u8 = guard.state().fetch_or(ALIVE_FLAG, atomic::Ordering::AcqRel);
Self { ptr: guard.ptr }
}
}
impl<T> Guard<'_, T> {
#[inline]
pub unsafe fn into_owned(guard: Guard<'_, T>) -> OwnedGuard<T> {
let owned_guard = OwnedGuard { ptr: guard.ptr };
mem::forget(guard);
owned_guard
}
#[inline]
pub fn unlock(guard: Self) {
drop(guard);
}
}
impl<T> OwnedGuard<T> {
#[inline]
pub fn new(value: T) -> OwnedGuard<T> {
OwnedGuard {
ptr: Inner::new(value, 0),
}
}
#[inline]
pub fn unlock(guard: Self) -> Bilock<T> {
let _: u8 = guard
.state()
.fetch_or(UNLOCKED_FLAG, atomic::Ordering::Release);
let bilock = Bilock { ptr: guard.ptr };
mem::forget(guard);
bilock
}
}
impl<T> Inner<T> {
#[inline]
fn new(value: T, state: u8) -> ptr::NonNull<Self> {
let inner = unsafe {
let mut inner = Box::<Inner<T>>::new_uninit();
ptr::write(&raw mut (*inner.as_mut_ptr()).value, value);
ptr::write(
&raw mut (*inner.as_mut_ptr()).state,
atomic::AtomicU8::new(state),
);
inner.assume_init()
};
unsafe { ptr::NonNull::new_unchecked(Box::into_raw(inner)) }
}
}
impl<T> ops::Deref for Guard<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &(*self.ptr.as_ptr()).value }
}
}
impl<T> ops::DerefMut for Guard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut (*self.ptr.as_ptr()).value }
}
}
impl<T> ops::Deref for OwnedGuard<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &(*self.ptr.as_ptr()).value }
}
}
impl<T> ops::DerefMut for OwnedGuard<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut (*self.ptr.as_ptr()).value }
}
}
impl<T> Drop for Bilock<T> {
fn drop(&mut self) {
let old_state = self
.state()
.fetch_and(!ALIVE_FLAG, atomic::Ordering::AcqRel);
if old_state & ALIVE_FLAG != ALIVE_FLAG {
drop(unsafe { Box::from_raw(self.ptr.as_ptr()) });
}
}
}
impl<T> Drop for Guard<'_, T> {
#[inline]
fn drop(&mut self) {
let _: u8 = self
.state()
.fetch_or(UNLOCKED_FLAG, atomic::Ordering::Release);
}
}
impl<T> Drop for OwnedGuard<T> {
fn drop(&mut self) {
drop(Self::unlock(Self { ptr: self.ptr }));
}
}
mod private {
use super::*;
pub trait BilockLike {
fn state(&self) -> &atomic::AtomicU8;
fn value(&self) -> *const ();
}
impl<T: BilockLike> BilockLike for &T {
#[inline]
fn state(&self) -> &atomic::AtomicU8 {
T::state(self)
}
#[inline]
fn value(&self) -> *const () {
T::value(self)
}
}
impl<T> BilockLike for Bilock<T> {
#[inline]
fn state(&self) -> &atomic::AtomicU8 {
unsafe { &(*self.ptr.as_ptr()).state }
}
#[inline]
fn value(&self) -> *const () {
unsafe { ptr::addr_of!((*self.ptr.as_ptr()).value).cast() }
}
}
impl<T> BilockLike for Guard<'_, T> {
#[inline]
fn state(&self) -> &atomic::AtomicU8 {
unsafe { &(*self.ptr.as_ptr()).state }
}
#[inline]
fn value(&self) -> *const () {
unsafe { ptr::addr_of!((*self.ptr.as_ptr()).value).cast() }
}
}
impl<T> BilockLike for OwnedGuard<T> {
#[inline]
fn state(&self) -> &atomic::AtomicU8 {
unsafe { &(*self.ptr.as_ptr()).state }
}
#[inline]
fn value(&self) -> *const () {
unsafe { ptr::addr_of!((*self.ptr.as_ptr()).value).cast() }
}
}
}
const ALIVE_FLAG: u8 = 1 << 0;
const UNLOCKED_FLAG: u8 = 1 << 1;