use std::cell::Cell;
use std::fmt;
use std::future::Future;
use std::marker;
use std::ops;
use std::pin::Pin;
use std::task::{Context, Poll};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AccessError {
#[error("expected data of type `{expected}`, but found `{actual}`")]
UnexpectedType {
expected: &'static str,
actual: &'static str,
},
#[error("{error}")]
NotAccessibleRef {
#[from]
error: NotAccessibleRef,
},
#[error("{error}")]
NotAccessibleMut {
#[from]
error: NotAccessibleMut,
},
#[error("{error}")]
NotAccessibleTake {
#[from]
error: NotAccessibleTake,
},
}
#[derive(Debug, Error)]
#[error("not accessible for shared access")]
pub struct NotAccessibleRef(());
#[derive(Debug, Error)]
#[error("not accessible for exclusive access")]
pub struct NotAccessibleMut(());
#[derive(Debug, Error)]
#[error("not accessible for taking")]
pub struct NotAccessibleTake(());
#[derive(Clone)]
pub(crate) struct Access(Cell<isize>);
impl Access {
pub(crate) const fn new() -> Self {
Self(Cell::new(0))
}
#[inline]
pub(crate) fn is_shared(&self) -> bool {
self.0.get().wrapping_sub(1) < 0
}
#[inline]
pub(crate) fn is_exclusive(&self) -> bool {
self.0.get() == 0
}
#[inline]
pub(crate) fn is_taken(&self) -> bool {
self.0.get() == isize::max_value()
}
#[inline]
pub(crate) fn shared(&self) -> Result<RawBorrowedRef, NotAccessibleRef> {
let b = self.0.get().wrapping_sub(1);
if b >= 0 {
return Err(NotAccessibleRef(()));
}
self.0.set(b);
Ok(RawBorrowedRef { access: self })
}
#[inline]
pub(crate) fn exclusive(&self) -> Result<RawBorrowedMut, NotAccessibleMut> {
let b = self.0.get().wrapping_add(1);
if b != 1 {
return Err(NotAccessibleMut(()));
}
self.0.set(b);
Ok(RawBorrowedMut { access: self })
}
#[inline]
pub(crate) fn take(&self) -> Result<RawTakeGuard, NotAccessibleTake> {
if self.0.get() != 0 {
return Err(NotAccessibleTake(()));
}
self.0.set(isize::max_value());
Ok(RawTakeGuard { access: self })
}
#[inline]
fn release_shared(&self) {
let b = self.0.get().wrapping_add(1);
debug_assert!(b <= 0);
self.0.set(b);
}
#[inline]
fn release_exclusive(&self) {
let b = self.0.get().wrapping_sub(1);
debug_assert!(b == 0);
self.0.set(b);
}
#[inline]
fn release_take(&self) {
let b = self.0.get();
debug_assert!(b == isize::max_value());
self.0.set(0);
}
}
impl fmt::Debug for Access {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
const TAKEN: isize = isize::max_value();
match self.0.get() {
0 => {
write!(fmt, "fully accessible")?;
}
1 => {
write!(fmt, "exclusively accessed")?;
}
TAKEN => {
write!(fmt, "moved")?;
}
n if n < 0 => {
write!(fmt, "shared by `{}`", -n)?;
}
n => {
write!(fmt, "illegal access state `{}`", n)?;
}
}
Ok(())
}
}
pub struct RawBorrowedRef {
access: *const Access,
}
impl Drop for RawBorrowedRef {
fn drop(&mut self) {
unsafe { (*self.access).release_shared() };
}
}
pub struct BorrowRef<'a, T: ?Sized + 'a> {
data: *const T,
guard: RawBorrowedRef,
_marker: marker::PhantomData<&'a T>,
}
impl<'a, T: ?Sized> BorrowRef<'a, T> {
pub(crate) unsafe fn from_raw(data: *const T, guard: RawBorrowedRef) -> Self {
Self {
data,
guard,
_marker: marker::PhantomData,
}
}
pub fn try_map<M, U: ?Sized, E>(this: Self, m: M) -> Result<BorrowRef<'a, U>, E>
where
M: FnOnce(&T) -> Result<&U, E>,
{
let data = m(unsafe { &*this.data })?;
let guard = this.guard;
Ok(BorrowRef {
data,
guard,
_marker: marker::PhantomData,
})
}
}
impl<T: ?Sized> ops::Deref for BorrowRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.data }
}
}
impl<T: ?Sized> fmt::Debug for BorrowRef<'_, T>
where
T: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, fmt)
}
}
pub struct RawBorrowedMut {
access: *const Access,
}
impl Drop for RawBorrowedMut {
fn drop(&mut self) {
unsafe { (*self.access).release_exclusive() }
}
}
pub(crate) struct RawTakeGuard {
access: *const Access,
}
impl Drop for RawTakeGuard {
fn drop(&mut self) {
unsafe { (*self.access).release_take() }
}
}
pub struct BorrowMut<'a, T: ?Sized> {
data: *mut T,
guard: RawBorrowedMut,
_marker: marker::PhantomData<&'a mut T>,
}
impl<'a, T: ?Sized> BorrowMut<'a, T> {
pub(crate) unsafe fn from_raw(data: *mut T, guard: RawBorrowedMut) -> Self {
Self {
data,
guard,
_marker: marker::PhantomData,
}
}
pub fn try_map<M, U: ?Sized, E>(this: Self, m: M) -> Result<BorrowMut<'a, U>, E>
where
M: FnOnce(&mut T) -> Result<&mut U, E>,
{
let data = m(unsafe { &mut *this.data })?;
let guard = this.guard;
Ok(BorrowMut {
data,
guard,
_marker: marker::PhantomData,
})
}
}
impl<T: ?Sized> ops::Deref for BorrowMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.data }
}
}
impl<T: ?Sized> ops::DerefMut for BorrowMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.data }
}
}
impl<T: ?Sized> fmt::Debug for BorrowMut<'_, T>
where
T: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, fmt)
}
}
impl<F> Future for BorrowMut<'_, F>
where
F: Unpin + Future,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
Pin::new(&mut **this).poll(cx)
}
}