call_once/
lib.rs

1//! This crate provides [`CallOnce`], a simple, thread-safe type that can only be called successfully _once_:
2//!
3//! ```
4//! use call_once::CallOnce;
5//!
6//! static CALL_ONCE: CallOnce = CallOnce::new();
7//!
8//! assert!(CALL_ONCE.call_once().is_ok());
9//! assert!(CALL_ONCE.call_once().is_err());
10//! ```
11
12#![no_std]
13
14use core::fmt;
15use core::sync::atomic::{AtomicBool, Ordering};
16
17/// A type that can only be called successfully _once_.
18///
19/// This is a simple wrapper around an [`AtomicBool`] with a more descriptive API.
20///
21/// <div class="warning">
22/// While <code>CallOnce</code> is synchronized and thread-safe, it does not synchronize other memory accesses.
23/// </div>
24///
25/// # Examples
26///
27/// ```
28/// use call_once::CallOnce;
29///
30/// static CALL_ONCE: CallOnce = CallOnce::new();
31///
32/// assert!(CALL_ONCE.call_once().is_ok());
33/// assert!(CALL_ONCE.call_once().is_err());
34/// ```
35#[derive(Default, Debug)]
36pub struct CallOnce {
37    called: AtomicBool,
38}
39
40impl CallOnce {
41    /// Creates a new `CallOnce`.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use call_once::CallOnce;
47    ///
48    /// let call_once = CallOnce::new();
49    /// ```
50    #[inline]
51    pub const fn new() -> Self {
52        Self {
53            called: AtomicBool::new(false),
54        }
55    }
56
57    /// Mark this `CallOnce` as called.
58    ///
59    /// Only the first call returns `Ok`.
60    /// All subsequent calls return `Err`.
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// use call_once::CallOnce;
66    ///
67    /// let call_once = CallOnce::new();
68    ///
69    /// assert!(call_once.call_once().is_ok());
70    /// assert!(call_once.call_once().is_err());
71    /// ```
72    #[inline]
73    pub fn call_once(&self) -> Result<(), CallOnceError> {
74        self.called
75            .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
76            .map(drop)
77            .map_err(|_| CallOnceError)
78    }
79
80    /// Returns `true` if `call_once` has been called.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use call_once::CallOnce;
86    ///
87    /// let call_once = CallOnce::new();
88    /// assert!(!call_once.was_called());
89    ///
90    /// call_once.call_once().unwrap();
91    /// assert!(call_once.was_called());
92    /// ```
93    #[inline]
94    pub fn was_called(&self) -> bool {
95        self.called.load(Ordering::Relaxed)
96    }
97}
98
99/// The `CallOnceError` error indicates that [`CallOnce::call_once`] has been called more than once.
100#[derive(Debug)]
101pub struct CallOnceError;
102
103impl fmt::Display for CallOnceError {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.write_str("call_once was executed more than once")
106    }
107}