#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
#[doc(hidden)]
pub extern crate core as std_core;
#[cfg(feature = "std")]
#[doc(hidden)]
pub extern crate std as std_core;
use self::std_core::cell::UnsafeCell;
use self::std_core::fmt;
#[cfg(feature = "std")]
mod arc;
#[cfg(feature = "std")]
mod rc;
#[cfg(feature = "std")]
pub use self::{arc::*, rc::*};
mod singleton_factory;
mod singleton;
pub use self::{singleton::*, singleton_factory::*};
pub unsafe trait Token<Keyhole> {
fn eq_id(&self, id: &Keyhole) -> bool;
}
pub unsafe trait Unsync {}
#[derive(Default)]
pub struct TokenLock<T: ?Sized, Keyhole> {
keyhole: Keyhole,
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send, Keyhole: Send> Send for TokenLock<T, Keyhole> {}
unsafe impl<T: ?Sized + Send + Sync, Keyhole: Sync> Sync for TokenLock<T, Keyhole> {}
#[derive(Default)]
pub struct UnsyncTokenLock<T: ?Sized, Keyhole> {
keyhole: Keyhole,
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send, Keyhole: Send> Send for UnsyncTokenLock<T, Keyhole> {}
unsafe impl<T: ?Sized + Send, Keyhole: Sync> Sync for UnsyncTokenLock<T, Keyhole> {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BadTokenError;
#[cfg(feature = "std")]
impl std::error::Error for BadTokenError {
fn description(&self) -> &str {
"token mismatch"
}
}
#[cfg(feature = "std")]
impl fmt::Display for BadTokenError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "token mismatch")
}
}
macro_rules! impl_common {
($ty:ident, [$($token_read_bounds:tt)*]) => {
impl<T: ?Sized, Keyhole: fmt::Debug> fmt::Debug for $ty<T, Keyhole> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(stringify!($ty))
.field("keyhole", &self.keyhole)
.finish()
}
}
impl<T, Keyhole> $ty<T, Keyhole> {
#[inline]
pub const fn new(keyhole: Keyhole, data: T) -> Self {
Self {
keyhole,
data: UnsafeCell::new(data),
}
}
#[inline]
pub fn into_inner(self) -> T {
self.data.into_inner()
}
}
impl<T: ?Sized, Keyhole> $ty<T, Keyhole> {
#[inline]
pub const fn keyhole(&self) -> &Keyhole {
&self.keyhole
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.data.get() }
}
#[inline]
pub const fn as_ptr(&self) -> *mut T {
self.data.get()
}
#[inline]
pub fn read<'a, K: $($token_read_bounds)*>(&'a self, token: &'a K) -> &'a T {
self.try_read(token).unwrap()
}
#[inline]
pub fn write<'a, K: Token<Keyhole>>(&'a self, token: &'a mut K) -> &'a mut T {
self.try_write(token).unwrap()
}
#[inline]
pub fn try_read<'a, K: $($token_read_bounds)*>(
&'a self,
token: &'a K,
) -> Result<&'a T, BadTokenError> {
if token.eq_id(&self.keyhole) {
Ok(unsafe { &*self.data.get() })
} else {
Err(BadTokenError)
}
}
#[inline]
pub fn try_write<'a, K: Token<Keyhole>>(
&'a self,
token: &'a mut K,
) -> Result<&'a mut T, BadTokenError> {
if token.eq_id(&self.keyhole) {
Ok(unsafe { &mut *self.data.get() })
} else {
Err(BadTokenError)
}
}
}
impl<T, Keyhole> $ty<T, Keyhole> {
#[inline]
pub fn get<K: $($token_read_bounds)*>(&self, token: &K) -> T
where
T: Clone,
{
self.read(token).clone()
}
#[inline]
pub fn try_get<K: $($token_read_bounds)*>(&self, token: &K) -> Result<T, BadTokenError>
where
T: Clone,
{
Ok(self.try_read(token)?.clone())
}
#[inline]
pub fn take<K: Token<Keyhole>>(&self, token: &mut K) -> T
where
T: Default,
{
self.replace_with(token, |_| Default::default())
}
#[inline]
pub fn try_take<K: Token<Keyhole>>(&self, token: &mut K) -> Result<T, BadTokenError>
where
T: Default,
{
self.try_replace_with(token, |_| Default::default())
}
#[inline]
pub fn replace<K: Token<Keyhole>>(&self, token: &mut K, t: T) -> T {
std_core::mem::replace(self.write(token), t)
}
#[inline]
pub fn replace_with<K: Token<Keyhole>>(
&self,
token: &mut K,
f: impl FnOnce(&mut T) -> T,
) -> T {
self.try_replace_with(token, f).unwrap()
}
#[inline]
pub fn try_replace_with<K: Token<Keyhole>>(
&self,
token: &mut K,
f: impl FnOnce(&mut T) -> T,
) -> Result<T, BadTokenError> {
let inner = self.try_write(token)?;
let new = f(inner);
Ok(std_core::mem::replace(inner, new))
}
#[inline]
pub fn swap<IOther, K: Token<Keyhole> + Token<IOther>>(
&self,
token: &mut K,
other: &$ty<T, IOther>,
) {
self.try_swap(token, other).unwrap()
}
#[inline]
pub fn try_swap<
IOther,
K: Token<Keyhole> + Token<IOther>>(
&self,
token: &mut K,
other: &$ty<T, IOther>,
) -> Result<(), BadTokenError> {
let _ = self.try_write(token)?;
let _ = other.try_write(token)?;
unsafe {
std_core::ptr::swap(self.as_ptr(), other.as_ptr());
}
Ok(())
}
}
impl<T: Clone, Keyhole: Clone> $ty<T, Keyhole> {
#[inline]
pub fn clone<K: $($token_read_bounds)*>(&self, token: &K) -> Self {
let value = self.get(token);
Self::new(self.keyhole.clone(), value)
}
#[inline]
pub fn try_clone<K: $($token_read_bounds)*>(&self, token: &K) -> Result<Self, BadTokenError> {
let value = self.try_get(token)?;
Ok(Self::new(self.keyhole.clone(), value))
}
}
};
}
impl_common!(TokenLock, [Token<Keyhole>]);
impl_common!(UnsyncTokenLock, [Token<Keyhole> + Unsync]);
#[test]
#[cfg(feature = "std")]
fn basic() {
let mut token = ArcToken::new();
let lock = TokenLock::new(token.id(), 1);
assert_eq!(*lock.read(&token), 1);
let guard = lock.write(&mut token);
assert_eq!(*guard, 1);
}
#[test]
#[cfg(feature = "std")]
fn bad_token() {
let token1 = ArcToken::new();
let mut token2 = ArcToken::new();
let lock = TokenLock::new(token1.id(), 1);
assert_eq!(lock.try_write(&mut token2), Err(BadTokenError));
}
#[test]
#[cfg(feature = "std")]
fn unsend_basic() {
let mut token = RcToken::new();
let lock = UnsyncTokenLock::new(token.id(), 1);
assert_eq!(*lock.read(&token), 1);
let guard = lock.write(&mut token);
assert_eq!(*guard, 1);
}
#[test]
#[cfg(feature = "std")]
fn unsend_bad_token() {
let token1 = RcToken::new();
let mut token2 = RcToken::new();
let lock = UnsyncTokenLock::new(token1.id(), 1);
assert_eq!(lock.try_write(&mut token2), Err(BadTokenError));
}