1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
use crate::std_core::{fmt, mem, ops, sync::atomic::AtomicBool};

use crate::singleton::{SingletonToken, SingletonTokenVariant, SyncVariant, UnsyncVariant};

/// The RAII guard for a [`SingletonToken`] obtained through
/// [`SingletonToken::new`]. Returns the token to the factory automatically
/// when dropped.
///
/// The second type parameter (`Variant`) is internal use only and exempt from
/// the API stability guarantee.
pub struct SingletonTokenGuard<Tag: ?Sized + SingletonTokenFactory, Variant = SyncVariant> {
    token: SingletonToken<Tag, Variant>,
}

/// The `!Sync` variant of [`SingletonTokenGuard`].
pub type UnsyncSingletonTokenGuard<Tag> = SingletonTokenGuard<Tag, UnsyncVariant>;

/// Associates a type with a flag indicating whether an instance of
/// [`SingletonToken`]`<Self>` is present.
///
/// Enables the [`SingletonToken`]`::<Self>::`[`new`] method.
///
/// Use [`impl_singleton_token_factory!`] to implement this trait.
///
/// # Safety
///
/// Implementing this trait incorrectly might break [`Token`]'s invariants.
///
/// [`Token`]: crate::Token
/// [`new`]: SingletonToken::new
pub unsafe trait SingletonTokenFactory {
    /// Take a token. Returns `true` if successful.
    fn take() -> bool;
    /// Return a token.
    ///
    /// # Safety
    ///
    /// Each call to this method must be accompanied with the destruction of an
    /// actually-existing instance of `SingletonToken`.
    unsafe fn r#return();
}

/// Implement [`SingletonTokenFactory`] on a given type.
///
/// The generated implementation uses `AtomicBool` and therefore might not
/// compile on some targets.
#[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)
            }
            // The inner `unsafe` block is redundant only if `unsafe_op_in_unsafe_fn`
            // (https://github.com/rust-lang/rust/issues/71668) is set to "allow".
            #[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);
            }
        }
    };
}

/// Internal use only
#[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) {
        // Safety: This call is accompanied with the destruction of `self.token`.
        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
    }
}

/// Error type returned when [`SingletonToken::new`] was called, but a token
/// has already been issued, and a new one cannot be issued until the old one
/// is returned to the factory by dropping [`SingletonTokenGuard`].
#[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>
{
    /// Construct `Self`, using `Tag`'s [`SingletonTokenFactory`] implementation
    /// to ensure only one token is present at once.
    ///
    /// Returns an RAII guard that derefs to `SingletonToken` and returns the
    /// token when dropped.
    ///
    /// # Examples
    ///
    /// ```
    /// # use tokenlock::*;
    /// struct MyTag;
    /// impl_singleton_token_factory!(MyTag);
    ///
    /// type MyTokenLock<T> = TokenLock<T, SingletonTokenId<MyTag>>;
    /// type MyToken = SingletonToken<MyTag>;
    /// type MyTokenId = SingletonTokenId<MyTag>;
    ///
    /// static LOCK1: MyTokenLock<u32> = MyTokenLock::new(MyTokenId::new(), 1);
    /// static LOCK2: MyTokenLock<u32> = MyTokenLock::new(MyTokenId::new(), 1);
    ///
    /// // Create a singleton token with a runtime uniqueness check
    /// let mut token = MyToken::new().unwrap();
    ///
    /// LOCK1.read(&*token);
    /// LOCK2.write(&mut *token);
    /// ```
    ///
    /// The `SingletonTokenFactory` implementation remembers that a token
    /// has been issued, so attempts to issue two tokens of the same tag will
    /// fail:
    ///
    /// ```
    /// # use tokenlock::*;
    /// # struct MyTag;
    /// # impl_singleton_token_factory!(MyTag);
    /// let token = SingletonToken::<MyTag>::new().unwrap();
    /// assert!(SingletonToken::<MyTag>::new().is_err());
    /// ```
    #[inline]
    pub fn new() -> Result<SingletonTokenGuard<Tag, Variant>, SingletonTokenExhaustedError> {
        if Tag::take() {
            Ok(SingletonTokenGuard {
                // Safety: We established by calling `Tag::take` that the token
                //         doesn't exist yet
                token: unsafe { SingletonToken::new_unchecked() },
            })
        } else {
            Err(SingletonTokenExhaustedError)
        }
    }
}

impl<Tag: ?Sized + SingletonTokenFactory> SingletonTokenGuard<Tag> {
    /// Convert `SingletonTokenGuard` to the `!Sync` variant.
    #[inline]
    pub fn into_unsync(self) -> UnsyncSingletonTokenGuard<Tag> {
        // Suppress `self`'s destructor
        mem::forget(self);

        SingletonTokenGuard {
            // Safety: The previous token has just been removed
            token: unsafe { SingletonToken::new_unchecked() },
        }
    }
}

impl<Tag: ?Sized + SingletonTokenFactory> UnsyncSingletonTokenGuard<Tag> {
    /// Convert `UnsyncSingletonToken` to the `Sync` variant.
    #[inline]
    pub fn into_sync(self) -> SingletonTokenGuard<Tag> {
        // Suppress `self`'s destructor
        mem::forget(self);

        SingletonTokenGuard {
            // Safety: The previous token has just been removed
            token: unsafe { SingletonToken::new_unchecked() },
        }
    }
}