lock_free_static/
static_.rs

1use crate::{OnceCell, OnceMut};
2use core::ops::Deref;
3
4/// Convenience wrapper for static initialization of cells.
5///
6/// Should be explicitly initialized (by [`init`](LockFreeStatic::init) call) because
7/// initialization is fallible and therefore cannot be done automatically on dereference.
8pub struct LockFreeStatic<T, D, F: Fn() -> T = fn() -> T> {
9    base: D,
10    ctor: F,
11}
12
13impl<T, D, F: Fn() -> T> Deref for LockFreeStatic<T, D, F> {
14    type Target = D;
15    fn deref(&self) -> &Self::Target {
16        &self.base
17    }
18}
19
20impl<T, D, F: Fn() -> T> LockFreeStatic<T, D, F> {
21    /// Creates a new wrapper.
22    pub const fn new(base: D, ctor: F) -> Self {
23        Self { base, ctor }
24    }
25}
26
27impl<T, F: Fn() -> T> LockFreeStatic<T, OnceCell<T>, F> {
28    /// Initializes the underlying cell.
29    ///
30    /// `true` is returned if the cell was initialized by this call.
31    #[must_use]
32    pub fn init(&self) -> bool {
33        self.base.set((self.ctor)()).is_ok()
34    }
35}
36
37impl<T, F: Fn() -> T> LockFreeStatic<T, OnceMut<T>, F> {
38    /// Initializes the underlying cell.
39    ///
40    /// `true` is returned if the cell was initialized by this call.
41    #[must_use]
42    pub fn init(&self) -> bool {
43        self.base.set((self.ctor)()).is_ok()
44    }
45}
46
47/// Convenience macro for creating lock-free statics.
48#[macro_export]
49macro_rules! lock_free_static {
50    ($(#[$attr:meta])* $vis:vis static $ident:ident: $ty:ty = $expr:expr; $($next:tt)*) => {
51        $(#[$attr])*
52        $vis static $ident: $crate::LockFreeStatic<$ty, $crate::OnceCell<$ty>>
53            = $crate::LockFreeStatic::new($crate::OnceCell::new(), || $expr);
54        $crate::lock_free_static!($($next)*);
55    };
56    ($(#[$attr:meta])* $vis:vis static mut $ident:ident: $ty:ty = $expr:expr; $($next:tt)*) => {
57        $(#[$attr])*
58        $vis static $ident: $crate::LockFreeStatic<$ty, $crate::OnceMut<$ty>>
59            = $crate::LockFreeStatic::new($crate::OnceMut::new(), || $expr);
60        $crate::lock_free_static!($($next)*);
61    };
62    () => {};
63}
64
65#[cfg(test)]
66mod tests {
67    use crate::lock_free_static;
68
69    lock_free_static! {
70        static CONST: i32 = 123;
71    }
72
73    #[test]
74    fn const_() {
75        assert!(CONST.get().is_none());
76        assert!(CONST.init());
77
78        let value = CONST.get().unwrap();
79        assert_eq!(*value, 123);
80        assert_eq!(*CONST.get().unwrap(), 123);
81    }
82
83    lock_free_static! {
84        static mut MUT: i32 = 123;
85    }
86
87    #[test]
88    fn mut_() {
89        assert!(MUT.lock().is_none());
90        assert!(MUT.init());
91
92        let mut guard = MUT.lock().unwrap();
93        assert_eq!(*guard, 123);
94        assert!(MUT.lock().is_none());
95        *guard = 321;
96        drop(guard);
97
98        assert_eq!(*MUT.lock().unwrap(), 321);
99    }
100
101    lock_free_static! {
102        static ONE: i32 = 1;
103        static mut TWO: i32 = 2;
104    }
105
106    #[test]
107    fn multiple() {
108        assert!(ONE.init());
109        assert!(TWO.init());
110        assert_eq!(*ONE.get().unwrap(), 1);
111        assert_eq!(*ONE.get().unwrap(), 1);
112        assert_eq!(*TWO.get_mut().unwrap(), 2);
113        assert!(TWO.get_mut().is_none());
114    }
115
116    mod outer {
117        use crate::lock_free_static;
118
119        lock_free_static! {
120            pub static ONE: i32 = 1;
121            pub static mut TWO: i32 = 2;
122        }
123    }
124
125    #[test]
126    fn visibility() {
127        assert!(outer::ONE.init());
128        assert!(outer::TWO.init());
129        assert_eq!(*outer::ONE.get().unwrap(), 1);
130        assert_eq!(*outer::TWO.get_mut().unwrap(), 2);
131    }
132}