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