embassy_sync/
once_lock.rs

1//! Synchronization primitive for initializing a value once, allowing others to await a reference to the value.
2
3use core::cell::Cell;
4use core::fmt::{Debug, Formatter};
5use core::future::{poll_fn, Future};
6use core::mem::MaybeUninit;
7use core::sync::atomic::{AtomicBool, Ordering};
8use core::task::Poll;
9
10/// The `OnceLock` is a synchronization primitive that allows for
11/// initializing a value once, and allowing others to `.await` a
12/// reference to the value. This is useful for lazy initialization of
13/// a static value.
14///
15/// **Note**: this implementation uses a busy loop to poll the value,
16/// which is not as efficient as registering a dedicated `Waker`.
17/// However, if the usecase for it is to initialize a static variable
18/// relatively early in the program life cycle, it should be fine.
19///
20/// # Example
21/// ```
22/// use futures_executor::block_on;
23/// use embassy_sync::once_lock::OnceLock;
24///
25/// // Define a static value that will be lazily initialized
26/// static VALUE: OnceLock<u32> = OnceLock::new();
27///
28/// let f = async {
29///
30/// // Initialize the value
31/// let reference = VALUE.get_or_init(|| 20);
32/// assert_eq!(reference, &20);
33///
34/// // Wait for the value to be initialized
35/// // and get a static reference it
36/// assert_eq!(VALUE.get().await, &20);
37///
38/// };
39/// block_on(f)
40/// ```
41pub struct OnceLock<T> {
42    init: AtomicBool,
43    data: Cell<MaybeUninit<T>>,
44}
45
46impl<T> Debug for OnceLock<T> {
47    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
48        f.debug_struct("OnceLock")
49            .field("init", &self.init)
50            .field("data", &"Cell<MaybeUninit<{unprintable}>>")
51            .finish()
52    }
53}
54
55unsafe impl<T> Sync for OnceLock<T> where T: Sync {}
56
57impl<T> OnceLock<T> {
58    /// Create a new uninitialized `OnceLock`.
59    pub const fn new() -> Self {
60        Self {
61            init: AtomicBool::new(false),
62            data: Cell::new(MaybeUninit::zeroed()),
63        }
64    }
65
66    /// Get a reference to the underlying value, waiting for it to be set.
67    /// If the value is already set, this will return immediately.
68    pub fn get(&self) -> impl Future<Output = &T> {
69        poll_fn(|cx| match self.try_get() {
70            Some(data) => Poll::Ready(data),
71            None => {
72                cx.waker().wake_by_ref();
73                Poll::Pending
74            }
75        })
76    }
77
78    /// Try to get a reference to the underlying value if it exists.
79    pub fn try_get(&self) -> Option<&T> {
80        if self.init.load(Ordering::Relaxed) {
81            Some(unsafe { self.get_ref_unchecked() })
82        } else {
83            None
84        }
85    }
86
87    /// Set the underlying value. If the value is already set, this will return an error with the given value.
88    pub fn init(&self, value: T) -> Result<(), T> {
89        // Critical section is required to ensure that the value is
90        // not simultaneously initialized elsewhere at the same time.
91        critical_section::with(|_| {
92            // If the value is not set, set it and return Ok.
93            if !self.init.load(Ordering::Relaxed) {
94                self.data.set(MaybeUninit::new(value));
95                self.init.store(true, Ordering::Relaxed);
96                Ok(())
97
98            // Otherwise return an error with the given value.
99            } else {
100                Err(value)
101            }
102        })
103    }
104
105    /// Get a reference to the underlying value, initializing it if it does not exist.
106    pub fn get_or_init<F>(&self, f: F) -> &T
107    where
108        F: FnOnce() -> T,
109    {
110        // Critical section is required to ensure that the value is
111        // not simultaneously initialized elsewhere at the same time.
112        critical_section::with(|_| {
113            // If the value is not set, set it.
114            if !self.init.load(Ordering::Relaxed) {
115                self.data.set(MaybeUninit::new(f()));
116                self.init.store(true, Ordering::Relaxed);
117            }
118        });
119
120        // Return a reference to the value.
121        unsafe { self.get_ref_unchecked() }
122    }
123
124    /// Consume the `OnceLock`, returning the underlying value if it was initialized.
125    pub fn into_inner(self) -> Option<T> {
126        if self.init.load(Ordering::Relaxed) {
127            Some(unsafe { self.data.into_inner().assume_init() })
128        } else {
129            None
130        }
131    }
132
133    /// Take the underlying value if it was initialized, uninitializing the `OnceLock` in the process.
134    pub fn take(&mut self) -> Option<T> {
135        // If the value is set, uninitialize the lock and return the value.
136        critical_section::with(|_| {
137            if self.init.load(Ordering::Relaxed) {
138                let val = unsafe { self.data.replace(MaybeUninit::zeroed()).assume_init() };
139                self.init.store(false, Ordering::Relaxed);
140                Some(val)
141
142            // Otherwise return None.
143            } else {
144                None
145            }
146        })
147    }
148
149    /// Check if the value has been set.
150    pub fn is_set(&self) -> bool {
151        self.init.load(Ordering::Relaxed)
152    }
153
154    /// Get a reference to the underlying value.
155    /// # Safety
156    /// Must only be used if a value has been set.
157    unsafe fn get_ref_unchecked(&self) -> &T {
158        (*self.data.as_ptr()).assume_init_ref()
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn once_lock() {
168        let lock = OnceLock::new();
169        assert_eq!(lock.try_get(), None);
170        assert_eq!(lock.is_set(), false);
171
172        let v = 42;
173        assert_eq!(lock.init(v), Ok(()));
174        assert_eq!(lock.is_set(), true);
175        assert_eq!(lock.try_get(), Some(&v));
176        assert_eq!(lock.try_get(), Some(&v));
177
178        let v = 43;
179        assert_eq!(lock.init(v), Err(v));
180        assert_eq!(lock.is_set(), true);
181        assert_eq!(lock.try_get(), Some(&42));
182    }
183
184    #[test]
185    fn once_lock_get_or_init() {
186        let lock = OnceLock::new();
187        assert_eq!(lock.try_get(), None);
188        assert_eq!(lock.is_set(), false);
189
190        let v = lock.get_or_init(|| 42);
191        assert_eq!(v, &42);
192        assert_eq!(lock.is_set(), true);
193        assert_eq!(lock.try_get(), Some(&42));
194
195        let v = lock.get_or_init(|| 43);
196        assert_eq!(v, &42);
197        assert_eq!(lock.is_set(), true);
198        assert_eq!(lock.try_get(), Some(&42));
199    }
200
201    #[test]
202    fn once_lock_static() {
203        static LOCK: OnceLock<i32> = OnceLock::new();
204
205        let v: &'static i32 = LOCK.get_or_init(|| 42);
206        assert_eq!(v, &42);
207
208        let v: &'static i32 = LOCK.get_or_init(|| 43);
209        assert_eq!(v, &42);
210    }
211
212    #[futures_test::test]
213    async fn once_lock_async() {
214        static LOCK: OnceLock<i32> = OnceLock::new();
215
216        assert!(LOCK.init(42).is_ok());
217
218        let v: &'static i32 = LOCK.get().await;
219        assert_eq!(v, &42);
220    }
221
222    #[test]
223    fn once_lock_into_inner() {
224        let lock: OnceLock<i32> = OnceLock::new();
225
226        let v = lock.get_or_init(|| 42);
227        assert_eq!(v, &42);
228
229        assert_eq!(lock.into_inner(), Some(42));
230    }
231
232    #[test]
233    fn once_lock_take_init() {
234        let mut lock: OnceLock<i32> = OnceLock::new();
235
236        assert_eq!(lock.get_or_init(|| 42), &42);
237        assert_eq!(lock.is_set(), true);
238
239        assert_eq!(lock.take(), Some(42));
240        assert_eq!(lock.is_set(), false);
241
242        assert_eq!(lock.get_or_init(|| 43), &43);
243        assert_eq!(lock.is_set(), true);
244    }
245}