use core::{fmt, mem, ops};
use crate::singleton::{SingletonToken, SingletonTokenVariant, SyncVariant, UnsyncVariant};
pub struct SingletonTokenGuard<Tag: ?Sized + SingletonTokenFactory, Variant = SyncVariant> {
token: SingletonToken<Tag, Variant>,
}
pub type UnsyncSingletonTokenGuard<Tag> = SingletonTokenGuard<Tag, UnsyncVariant>;
pub unsafe trait SingletonTokenFactory {
fn take() -> bool;
unsafe fn r#return();
}
#[cfg(any(
all(compiler_has_cfg_target_has_atomic, target_has_atomic = "8"),
not(compiler_has_cfg_target_has_atomic)
))]
#[cfg_attr(feature = "doc_cfg", doc(cfg(target_has_atomic = "8")))]
#[macro_export]
macro_rules! impl_singleton_token_factory {
($($ty:ty),* $(,)*) => {$(
impl $crate::SingletonTokenFactoryStorage for $ty {
#[inline]
unsafe fn __stfs_token_issued() -> &'static $crate::core::sync::atomic::AtomicBool {
static TOKEN_ISSUED: $crate::core::sync::atomic::AtomicBool =
$crate::core::sync::atomic::AtomicBool::new(false);
&TOKEN_ISSUED
}
}
unsafe impl $crate::SingletonTokenFactory for $ty {
#[inline]
fn take() -> $crate::core::primitive::bool {
let token_issued =
unsafe { <$ty as $crate::SingletonTokenFactoryStorage>::__stfs_token_issued() };
!token_issued.swap(true, $crate::core::sync::atomic::Ordering::Acquire)
}
#[allow(unused_unsafe)]
#[inline]
unsafe fn r#return() {
let token_issued =
unsafe { <$ty as $crate::SingletonTokenFactoryStorage>::__stfs_token_issued() };
token_issued.store(false, $crate::core::sync::atomic::Ordering::Release);
}
}
)*};
}
#[doc(hidden)]
#[cfg(any(
all(compiler_has_cfg_target_has_atomic, target_has_atomic = "8"),
not(compiler_has_cfg_target_has_atomic)
))]
pub trait SingletonTokenFactoryStorage {
unsafe fn __stfs_token_issued() -> &'static core::sync::atomic::AtomicBool;
}
impl<Tag: ?Sized + SingletonTokenFactory, Variant> Drop for SingletonTokenGuard<Tag, Variant> {
#[inline]
fn drop(&mut self) {
unsafe { Tag::r#return() };
}
}
impl<Tag: ?Sized + SingletonTokenFactory, Variant: SingletonTokenVariant> ops::Deref
for SingletonTokenGuard<Tag, Variant>
{
type Target = SingletonToken<Tag, Variant>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.token
}
}
impl<Tag: ?Sized + SingletonTokenFactory, Variant: SingletonTokenVariant> ops::DerefMut
for SingletonTokenGuard<Tag, Variant>
{
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.token
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SingletonTokenExhaustedError;
#[cfg(feature = "std")]
impl std::error::Error for SingletonTokenExhaustedError {
fn description(&self) -> &str {
"token already issued"
}
}
impl fmt::Display for SingletonTokenExhaustedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "token already issued")
}
}
impl<Tag: ?Sized + SingletonTokenFactory, Variant: SingletonTokenVariant>
SingletonToken<Tag, Variant>
{
#[inline]
pub fn new() -> Result<SingletonTokenGuard<Tag, Variant>, SingletonTokenExhaustedError> {
if Tag::take() {
Ok(SingletonTokenGuard {
token: unsafe { SingletonToken::new_unchecked() },
})
} else {
Err(SingletonTokenExhaustedError)
}
}
}
impl<Tag: ?Sized + SingletonTokenFactory> SingletonTokenGuard<Tag> {
#[inline]
pub fn into_unsync(self) -> UnsyncSingletonTokenGuard<Tag> {
mem::forget(self);
SingletonTokenGuard {
token: unsafe { SingletonToken::new_unchecked() },
}
}
}
impl<Tag: ?Sized + SingletonTokenFactory> UnsyncSingletonTokenGuard<Tag> {
#[inline]
pub fn into_sync(self) -> SingletonTokenGuard<Tag> {
mem::forget(self);
SingletonTokenGuard {
token: unsafe { SingletonToken::new_unchecked() },
}
}
}