Skip to main content

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    lock_free_static! {
68        static CONST: i32 = 123;
69    }
70
71    #[test]
72    fn const_() {
73        assert!(CONST.get().is_none());
74        assert!(CONST.init());
75
76        let value = CONST.get().unwrap();
77        assert_eq!(*value, 123);
78        assert_eq!(*CONST.get().unwrap(), 123);
79    }
80
81    lock_free_static! {
82        static mut MUT: i32 = 123;
83    }
84
85    #[test]
86    fn mut_() {
87        assert!(MUT.lock().is_none());
88        assert!(MUT.init());
89
90        let mut guard = MUT.lock().unwrap();
91        assert_eq!(*guard, 123);
92        assert!(MUT.lock().is_none());
93        *guard = 321;
94        drop(guard);
95
96        assert_eq!(*MUT.lock().unwrap(), 321);
97    }
98
99    lock_free_static! {
100        static ONE: i32 = 1;
101        static mut TWO: i32 = 2;
102    }
103
104    #[test]
105    fn multiple() {
106        assert!(ONE.init());
107        assert!(TWO.init());
108        assert_eq!(*ONE.get().unwrap(), 1);
109        assert_eq!(*ONE.get().unwrap(), 1);
110        assert_eq!(*TWO.get_mut().unwrap(), 2);
111        assert!(TWO.get_mut().is_none());
112    }
113
114    mod outer {
115        lock_free_static! {
116            pub static ONE: i32 = 1;
117            pub static mut TWO: i32 = 2;
118        }
119    }
120
121    #[test]
122    fn visibility() {
123        assert!(outer::ONE.init());
124        assert!(outer::TWO.init());
125        assert_eq!(*outer::ONE.get().unwrap(), 1);
126        assert_eq!(*outer::TWO.get_mut().unwrap(), 2);
127    }
128}