use crate::error;
use core::sync::atomic::{AtomicUsize, Ordering};
const HIGH_BIT: usize = !(usize::MAX >> 1);
const MAX_FAILED_BORROWS: usize = HIGH_BIT + (HIGH_BIT >> 1);
pub(super) struct BorrowState(AtomicUsize);
pub struct SharedBorrow<'a>(&'a BorrowState);
impl Drop for SharedBorrow<'_> {
#[inline]
fn drop(&mut self) {
(self.0).0.fetch_sub(1, Ordering::Release);
}
}
impl Clone for SharedBorrow<'_> {
#[inline]
fn clone(&self) -> Self {
self.0.read().unwrap()
}
}
pub struct ExclusiveBorrow<'a>(&'a BorrowState);
impl Drop for ExclusiveBorrow<'_> {
#[inline]
fn drop(&mut self) {
(self.0).0.store(0, Ordering::Release);
}
}
impl BorrowState {
#[inline]
pub(super) fn new() -> Self {
BorrowState(AtomicUsize::new(0))
}
#[inline]
pub(super) fn read(&self) -> Result<SharedBorrow<'_>, error::Borrow> {
let new = self.0.fetch_add(1, Ordering::Acquire) + 1;
if new & HIGH_BIT != 0 {
self.check_overflow(new);
Err(error::Borrow::Unique)
} else {
Ok(SharedBorrow(self))
}
}
#[allow(unused)]
#[inline]
pub(super) fn exclusive_read(&self) -> Result<SharedBorrow<'_>, error::Borrow> {
let old = match self
.0
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
{
Ok(x) => x,
Err(x) => x,
};
if old == 0 {
Ok(SharedBorrow(self))
} else if old & HIGH_BIT == 0 {
Err(error::Borrow::Shared)
} else {
Err(error::Borrow::Unique)
}
}
#[inline]
pub(super) fn write(&self) -> Result<ExclusiveBorrow<'_>, error::Borrow> {
let old = match self
.0
.compare_exchange(0, HIGH_BIT, Ordering::Acquire, Ordering::Relaxed)
{
Ok(x) => x,
Err(x) => x,
};
if old == 0 {
Ok(ExclusiveBorrow(self))
} else if old & HIGH_BIT == 0 {
Err(error::Borrow::Shared)
} else {
Err(error::Borrow::Unique)
}
}
#[cold]
#[inline(never)]
fn check_overflow(&self, new: usize) {
if new == HIGH_BIT {
self.0.fetch_sub(1, Ordering::Release);
panic!("too many immutable borrows");
} else if new >= MAX_FAILED_BORROWS {
struct ForceAbort;
impl Drop for ForceAbort {
fn drop(&mut self) {
panic!("Aborting to avoid unsound state of AtomicRefCell");
}
}
let _abort = ForceAbort;
panic!("Too many failed borrows");
}
}
}