use crate::std_core::{fmt, mem, ops, sync::atomic::AtomicBool};
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();
}
#[macro_export]
macro_rules! impl_singleton_token_factory {
($ty:ty $(,)*) => {
impl $crate::SingletonTokenFactoryStorage for $ty {
#[inline]
unsafe fn __stfs_token_issued() -> &'static $crate::std_core::sync::atomic::AtomicBool {
use $crate::std_core::sync::atomic::AtomicBool;
static TOKEN_ISSUED: AtomicBool = AtomicBool::new(false);
&TOKEN_ISSUED
}
}
unsafe impl $crate::SingletonTokenFactory for $ty {
#[inline]
fn take() -> bool {
use $crate::std_core::sync::atomic::Ordering;
let token_issued =
unsafe { <$ty as $crate::SingletonTokenFactoryStorage>::__stfs_token_issued() };
!token_issued.swap(true, Ordering::Acquire)
}
#[allow(unused_unsafe)]
#[inline]
unsafe fn r#return() {
use $crate::std_core::sync::atomic::Ordering;
let token_issued =
unsafe { <$ty as $crate::SingletonTokenFactoryStorage>::__stfs_token_issued() };
token_issued.store(false, Ordering::Release);
}
}
};
}
#[doc(hidden)]
pub trait SingletonTokenFactoryStorage {
unsafe fn __stfs_token_issued() -> &'static 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"
}
}
#[cfg(feature = "std")]
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() },
}
}
}