1//! Synchronization primitive for initializing a value once, allowing others to await a reference to the value.
23use core::cell::Cell;
4use core::future::{poll_fn, Future};
5use core::mem::MaybeUninit;
6use core::sync::atomic::{AtomicBool, Ordering};
7use core::task::Poll;
89/// The `OnceLock` is a synchronization primitive that allows for
10/// initializing a value once, and allowing others to `.await` a
11/// reference to the value. This is useful for lazy initialization of
12/// a static value.
13///
14/// **Note**: this implementation uses a busy loop to poll the value,
15/// which is not as efficient as registering a dedicated `Waker`.
16/// However, if the usecase for it is to initialize a static variable
17/// relatively early in the program life cycle, it should be fine.
18///
19/// # Example
20/// ```
21/// use futures_executor::block_on;
22/// use embassy_sync::once_lock::OnceLock;
23///
24/// // Define a static value that will be lazily initialized
25/// static VALUE: OnceLock<u32> = OnceLock::new();
26///
27/// let f = async {
28///
29/// // Initialize the value
30/// let reference = VALUE.get_or_init(|| 20);
31/// assert_eq!(reference, &20);
32///
33/// // Wait for the value to be initialized
34/// // and get a static reference it
35/// assert_eq!(VALUE.get().await, &20);
36///
37/// };
38/// block_on(f)
39/// ```
40pub struct OnceLock<T> {
41 init: AtomicBool,
42 data: Cell<MaybeUninit<T>>,
43}
4445unsafe impl<T> Sync for OnceLock<T> {}
4647impl<T> OnceLock<T> {
48/// Create a new uninitialized `OnceLock`.
49pub const fn new() -> Self {
50Self {
51 init: AtomicBool::new(false),
52 data: Cell::new(MaybeUninit::zeroed()),
53 }
54 }
5556/// Get a reference to the underlying value, waiting for it to be set.
57 /// If the value is already set, this will return immediately.
58pub fn get(&self) -> impl Future<Output = &T> {
59 poll_fn(|cx| match self.try_get() {
60Some(data) => Poll::Ready(data),
61None => {
62 cx.waker().wake_by_ref();
63 Poll::Pending
64 }
65 })
66 }
6768/// Try to get a reference to the underlying value if it exists.
69pub fn try_get(&self) -> Option<&T> {
70if self.init.load(Ordering::Relaxed) {
71Some(unsafe { self.get_ref_unchecked() })
72 } else {
73None
74}
75 }
7677/// Set the underlying value. If the value is already set, this will return an error with the given value.
78pub fn init(&self, value: T) -> Result<(), T> {
79// Critical section is required to ensure that the value is
80 // not simultaneously initialized elsewhere at the same time.
81critical_section::with(|_| {
82// If the value is not set, set it and return Ok.
83if !self.init.load(Ordering::Relaxed) {
84self.data.set(MaybeUninit::new(value));
85self.init.store(true, Ordering::Relaxed);
86Ok(())
8788// Otherwise return an error with the given value.
89} else {
90Err(value)
91 }
92 })
93 }
9495/// Get a reference to the underlying value, initializing it if it does not exist.
96pub fn get_or_init<F>(&self, f: F) -> &T
97where
98F: FnOnce() -> T,
99 {
100// Critical section is required to ensure that the value is
101 // not simultaneously initialized elsewhere at the same time.
102critical_section::with(|_| {
103// If the value is not set, set it.
104if !self.init.load(Ordering::Relaxed) {
105self.data.set(MaybeUninit::new(f()));
106self.init.store(true, Ordering::Relaxed);
107 }
108 });
109110// Return a reference to the value.
111unsafe { self.get_ref_unchecked() }
112 }
113114/// Consume the `OnceLock`, returning the underlying value if it was initialized.
115pub fn into_inner(self) -> Option<T> {
116if self.init.load(Ordering::Relaxed) {
117Some(unsafe { self.data.into_inner().assume_init() })
118 } else {
119None
120}
121 }
122123/// Take the underlying value if it was initialized, uninitializing the `OnceLock` in the process.
124pub fn take(&mut self) -> Option<T> {
125// If the value is set, uninitialize the lock and return the value.
126critical_section::with(|_| {
127if self.init.load(Ordering::Relaxed) {
128let val = unsafe { self.data.replace(MaybeUninit::zeroed()).assume_init() };
129self.init.store(false, Ordering::Relaxed);
130Some(val)
131132// Otherwise return None.
133} else {
134None
135}
136 })
137 }
138139/// Check if the value has been set.
140pub fn is_set(&self) -> bool {
141self.init.load(Ordering::Relaxed)
142 }
143144/// Get a reference to the underlying value.
145 /// # Safety
146 /// Must only be used if a value has been set.
147unsafe fn get_ref_unchecked(&self) -> &T {
148 (*self.data.as_ptr()).assume_init_ref()
149 }
150}
151152#[cfg(test)]
153mod tests {
154use super::*;
155156#[test]
157fn once_lock() {
158let lock = OnceLock::new();
159assert_eq!(lock.try_get(), None);
160assert_eq!(lock.is_set(), false);
161162let v = 42;
163assert_eq!(lock.init(v), Ok(()));
164assert_eq!(lock.is_set(), true);
165assert_eq!(lock.try_get(), Some(&v));
166assert_eq!(lock.try_get(), Some(&v));
167168let v = 43;
169assert_eq!(lock.init(v), Err(v));
170assert_eq!(lock.is_set(), true);
171assert_eq!(lock.try_get(), Some(&42));
172 }
173174#[test]
175fn once_lock_get_or_init() {
176let lock = OnceLock::new();
177assert_eq!(lock.try_get(), None);
178assert_eq!(lock.is_set(), false);
179180let v = lock.get_or_init(|| 42);
181assert_eq!(v, &42);
182assert_eq!(lock.is_set(), true);
183assert_eq!(lock.try_get(), Some(&42));
184185let v = lock.get_or_init(|| 43);
186assert_eq!(v, &42);
187assert_eq!(lock.is_set(), true);
188assert_eq!(lock.try_get(), Some(&42));
189 }
190191#[test]
192fn once_lock_static() {
193static LOCK: OnceLock<i32> = OnceLock::new();
194195let v: &'static i32 = LOCK.get_or_init(|| 42);
196assert_eq!(v, &42);
197198let v: &'static i32 = LOCK.get_or_init(|| 43);
199assert_eq!(v, &42);
200 }
201202#[futures_test::test]
203async fn once_lock_async() {
204static LOCK: OnceLock<i32> = OnceLock::new();
205206assert!(LOCK.init(42).is_ok());
207208let v: &'static i32 = LOCK.get().await;
209assert_eq!(v, &42);
210 }
211212#[test]
213fn once_lock_into_inner() {
214let lock: OnceLock<i32> = OnceLock::new();
215216let v = lock.get_or_init(|| 42);
217assert_eq!(v, &42);
218219assert_eq!(lock.into_inner(), Some(42));
220 }
221222#[test]
223fn once_lock_take_init() {
224let mut lock: OnceLock<i32> = OnceLock::new();
225226assert_eq!(lock.get_or_init(|| 42), &42);
227assert_eq!(lock.is_set(), true);
228229assert_eq!(lock.take(), Some(42));
230assert_eq!(lock.is_set(), false);
231232assert_eq!(lock.get_or_init(|| 43), &43);
233assert_eq!(lock.is_set(), true);
234 }
235}