Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use super::Once;
9use crate::loom::UnsafeCell;
10use core::{
11 fmt,
12 mem::MaybeUninit,
13 panic::{RefUnwindSafe, UnwindSafe},
14};
15use util::loom_const_fn;
16
17/// A synchronization primitive which can be written to only once.
18///
19/// Thread-safe variant of [`core::cell::OnceCell`].
20pub struct OnceLock<T> {
21 once: Once,
22 data: UnsafeCell<MaybeUninit<T>>,
23}
24
25impl<T> OnceLock<T> {
26 loom_const_fn! {
27 #[must_use]
28 pub const fn new() -> Self {
29 Self {
30 once: Once::new(),
31 data: UnsafeCell::new(MaybeUninit::uninit()),
32 }
33 }
34 }
35
36 /// # Panics
37 ///
38 /// Panics if the closure panics.
39 pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
40 self.once.call_once(|| {
41 self.data.with_mut(|data| {
42 // SAFETY: `Once` ensures this is only called once
43 unsafe { (*data).as_mut_ptr().write(f()) }
44 });
45 });
46
47 // SAFETY: `Once` ensures this is only called once
48 unsafe { self.force_get() }
49 }
50
51 /// # Errors
52 ///
53 /// Returns an error if the given closure errors.
54 ///
55 /// # Panics
56 ///
57 /// Panics if the closure panics.
58 pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
59 where
60 F: FnOnce() -> Result<T, E>,
61 {
62 let mut error = None;
63
64 self.once.call_once(|| {
65 #[allow(tail_expr_drop_order, reason = "")]
66 match f() {
67 Ok(val) => {
68 self.data.with_mut(|data| {
69 // SAFETY: `Once` ensures this is only called once
70 unsafe { (*data).as_mut_ptr().write(val) }
71 });
72 }
73 Err(err) => error = Some(err),
74 }
75 });
76
77 #[allow(if_let_rescope, reason = "")]
78 if let Some(err) = error {
79 Err(err)
80 } else {
81 // SAFETY: `Once` ensures this is only called once
82 unsafe { Ok(self.force_get()) }
83 }
84 }
85
86 /// Gets the reference to the underlying value.
87 ///
88 /// Returns `None` if the cell is empty, or being initialized. This
89 /// method never blocks.
90 pub fn get(&self) -> Option<&T> {
91 self.once.is_completed().then(|| {
92 // Safety: `is_completed` ensures value is properly initialized
93 unsafe { self.force_get() }
94 })
95 }
96
97 /// Gets the mutable reference to the underlying value.
98 ///
99 /// Returns `None` if the cell is empty. This method never blocks.
100 pub fn get_mut(&mut self) -> Option<&mut T> {
101 self.once.is_completed().then(|| {
102 // Safety: `is_completed` ensures value is properly initialized
103 unsafe { self.force_get_mut() }
104 })
105 }
106
107 /// Blocks the current thread until the cell is initialized.
108 pub fn wait(&self) -> &T {
109 self.once.wait();
110
111 // Safety: we have waited until the data is initialized
112 unsafe { self.force_get() }
113 }
114
115 /// Sets the contents of this cell to `value`.
116 ///
117 /// May block if another thread is currently attempting to initialize the cell. The cell is
118 /// guaranteed to contain a value when set returns, though not necessarily the one provided.
119 ///
120 /// Returns `Ok(())` if the cell's value was set by this call.
121 ///
122 /// # Errors
123 ///
124 /// Returns the value in the `Err` variant is the cell was full.
125 #[inline]
126 pub fn set(&self, value: T) -> Result<(), T> {
127 match self.try_insert(value) {
128 Ok(_) => Ok(()),
129 Err((_, value)) => Err(value),
130 }
131 }
132
133 /// Sets the contents of this cell to `value` if the cell was empty, then
134 /// returns a reference to it.
135 ///
136 /// May block if another thread is currently attempting to initialize the cell. The cell is
137 /// guaranteed to contain a value when set returns, though not necessarily the one provided.
138 ///
139 /// Returns `Ok(&value)` if the cell was empty
140 ///
141 /// # Errors
142 ///
143 /// Returns `Err(¤t_value, value)` if the cell was full.
144 #[inline]
145 pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
146 let mut value = Some(value);
147 let res = self.get_or_init(|| {
148 // Safety: we have initialized `value` to a `Some` above
149 unsafe { value.take().unwrap_unchecked() }
150 });
151 match value {
152 None => Ok(res),
153 Some(value) => Err((res, value)),
154 }
155 }
156
157 /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state.
158 ///
159 /// Has no effect and returns `None` if the `OnceLock` hasn't been initialized.
160 ///
161 /// Safety is guaranteed by requiring a mutable reference.
162 #[inline]
163 pub fn take(&mut self) -> Option<T> {
164 if self.is_initialized() {
165 self.once = Once::new();
166 self.data.with(|data| {
167 // SAFETY: `self.value` is initialized and contains a valid `T`.
168 // `self.once` is reset, so `is_initialized()` will be false again
169 // which prevents the value from being read twice.
170 Some(unsafe { MaybeUninit::assume_init_read(&*data) })
171 })
172 } else {
173 None
174 }
175 }
176
177 /// Consumes the `OnceLock`, returning the wrapped value. Returns
178 /// `None` if the cell was empty.
179 #[inline]
180 pub fn into_inner(mut self) -> Option<T> {
181 self.take()
182 }
183
184 #[inline]
185 fn is_initialized(&self) -> bool {
186 self.once.is_completed()
187 }
188
189 unsafe fn force_get(&self) -> &T {
190 self.data.with(|data| {
191 // SAFETY:
192 // * `UnsafeCell`/inner deref: data never changes again
193 // * `MaybeUninit`/outer deref: data was initialized
194 unsafe { &*(*data).as_ptr() }
195 })
196 }
197
198 unsafe fn force_get_mut(&mut self) -> &mut T {
199 self.data.with_mut(|data| {
200 // SAFETY:
201 // * `UnsafeCell`/inner deref: data never changes again
202 // * `MaybeUninit`/outer deref: data was initialized
203 unsafe { &mut *(*data).as_mut_ptr() }
204 })
205 }
206}
207
208// Safety: synchronization primitive
209unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
210// Safety: synchronization primitive
211unsafe impl<T: Send> Send for OnceLock<T> {}
212
213impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
214impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
215
216impl<T> Default for OnceLock<T> {
217 fn default() -> Self {
218 Self::new()
219 }
220}
221
222impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 let mut d = f.debug_tuple("OnceLock");
225 match self.get() {
226 Some(v) => d.field(v),
227 None => d.field(&format_args!("<uninit>")),
228 };
229 d.finish()
230 }
231}
232
233impl<T: Clone> Clone for OnceLock<T> {
234 #[inline]
235 fn clone(&self) -> OnceLock<T> {
236 let cell = Self::new();
237 if let Some(value) = self.get() {
238 match cell.set(value.clone()) {
239 Ok(()) => (),
240 Err(_) => unreachable!(),
241 }
242 }
243 cell
244 }
245}
246
247impl<T> From<T> for OnceLock<T> {
248 /// Creates a new cell with its contents set to `value`.
249 ///
250 /// # Example
251 ///
252 /// ```
253 /// use std::sync::OnceLock;
254 ///
255 /// # fn main() -> Result<(), i32> {
256 /// let a = OnceLock::from(3);
257 /// let b = OnceLock::new();
258 /// b.set(3)?;
259 /// assert_eq!(a, b);
260 /// Ok(())
261 /// # }
262 /// ```
263 #[inline]
264 fn from(value: T) -> Self {
265 let cell = Self::new();
266 match cell.set(value) {
267 Ok(()) => cell,
268 Err(_) => unreachable!(),
269 }
270 }
271}
272
273impl<T: PartialEq> PartialEq for OnceLock<T> {
274 /// Equality for two `OnceLock`s.
275 ///
276 /// Two `OnceLock`s are equal if they either both contain values and their
277 /// values are equal, or if neither contains a value.
278 ///
279 /// # Examples
280 ///
281 /// ```
282 /// use std::sync::OnceLock;
283 ///
284 /// let five = OnceLock::new();
285 /// five.set(5).unwrap();
286 ///
287 /// let also_five = OnceLock::new();
288 /// also_five.set(5).unwrap();
289 ///
290 /// assert!(five == also_five);
291 ///
292 /// assert!(OnceLock::<u32>::new() == OnceLock::<u32>::new());
293 /// ```
294 #[inline]
295 fn eq(&self, other: &OnceLock<T>) -> bool {
296 self.get() == other.get()
297 }
298}
299
300impl<T: Eq> Eq for OnceLock<T> {}
301
302#[expect(clippy::undocumented_unsafe_blocks, reason = "")]
303unsafe impl<#[may_dangle] T> Drop for OnceLock<T> {
304 #[inline]
305 fn drop(&mut self) {
306 if self.is_initialized() {
307 self.data.with_mut(|data| {
308 // SAFETY: The cell is initialized and being dropped, so it can't
309 // be accessed again. We also don't touch the `T` other than
310 // dropping it, which validates our usage of #[may_dangle].
311 unsafe { MaybeUninit::assume_init_drop(&mut *data) }
312 });
313 }
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use crate::loom::thread;
321 use crate::loom::{AtomicUsize, Ordering};
322 use std::sync::mpsc::channel;
323
324 fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R {
325 thread::spawn(f).join().unwrap()
326 }
327
328 #[test]
329 fn sync_once_cell() {
330 static ONCE_CELL: OnceLock<i32> = OnceLock::new();
331
332 assert!(ONCE_CELL.get().is_none());
333
334 spawn_and_wait(|| {
335 ONCE_CELL.get_or_init(|| 92);
336 assert_eq!(ONCE_CELL.get(), Some(&92));
337 });
338
339 ONCE_CELL.get_or_init(|| panic!("Kaboom!"));
340 assert_eq!(ONCE_CELL.get(), Some(&92));
341 }
342
343 #[test]
344 fn sync_once_cell_get_mut() {
345 let mut c = OnceLock::new();
346 assert!(c.get_mut().is_none());
347 c.set(90).unwrap();
348 *c.get_mut().unwrap() += 2;
349 assert_eq!(c.get_mut(), Some(&mut 92));
350 }
351
352 #[test]
353 fn sync_once_cell_drop() {
354 static DROP_CNT: AtomicUsize = AtomicUsize::new(0);
355 struct Dropper;
356 impl Drop for Dropper {
357 fn drop(&mut self) {
358 DROP_CNT.fetch_add(1, Ordering::SeqCst);
359 }
360 }
361
362 let x = OnceLock::new();
363 spawn_and_wait(move || {
364 x.get_or_init(|| Dropper);
365 assert_eq!(DROP_CNT.load(Ordering::SeqCst), 0);
366 drop(x);
367 });
368
369 assert_eq!(DROP_CNT.load(Ordering::SeqCst), 1);
370 }
371
372 #[test]
373 fn sync_once_cell_drop_empty() {
374 let x = OnceLock::<String>::new();
375 drop(x);
376 }
377
378 #[test]
379 fn clone() {
380 let s = OnceLock::new();
381 let c = s.clone();
382 assert!(c.get().is_none());
383
384 s.set("hello".to_string()).unwrap();
385 let c = s.clone();
386 assert_eq!(c.get().map(String::as_str), Some("hello"));
387 }
388
389 #[test]
390 fn from_impl() {
391 assert_eq!(OnceLock::from("value").get(), Some(&"value"));
392 assert_ne!(OnceLock::from("foo").get(), Some(&"bar"));
393 }
394
395 #[test]
396 fn partialeq_impl() {
397 assert!(OnceLock::from("value") == OnceLock::from("value"));
398 assert!(OnceLock::from("foo") != OnceLock::from("bar"));
399
400 assert!(OnceLock::<String>::new() == OnceLock::new());
401 assert!(OnceLock::<String>::new() != OnceLock::from("value".to_owned()));
402 }
403
404 #[test]
405 fn into_inner() {
406 let cell: OnceLock<String> = OnceLock::new();
407 assert_eq!(cell.into_inner(), None);
408 let cell = OnceLock::new();
409 cell.set("hello".to_string()).unwrap();
410 assert_eq!(cell.into_inner(), Some("hello".to_string()));
411 }
412
413 #[test]
414 fn is_sync_send() {
415 fn assert_traits<T: Send + Sync>() {}
416 assert_traits::<OnceLock<String>>();
417 }
418
419 #[test]
420 fn eval_once_macro() {
421 macro_rules! eval_once {
422 (|| -> $ty:ty {
423 $($body:tt)*
424 }) => {{
425 static ONCE_CELL: OnceLock<$ty> = OnceLock::new();
426 fn init() -> $ty {
427 $($body)*
428 }
429 ONCE_CELL.get_or_init(init)
430 }};
431 }
432
433 let fib: &'static Vec<i32> = eval_once! {
434 || -> Vec<i32> {
435 let mut res = vec![1, 1];
436 for i in 0..10 {
437 let next = res[i] + res[i + 1];
438 res.push(next);
439 }
440 res
441 }
442 };
443 assert_eq!(fib[5], 8)
444 }
445
446 #[test]
447 fn sync_once_cell_does_not_leak_partially_constructed_boxes() {
448 static ONCE_CELL: OnceLock<String> = OnceLock::new();
449
450 let n_readers = 10;
451 let n_writers = 3;
452 const MSG: &str = "Hello, World";
453
454 let (tx, rx) = channel();
455
456 for _ in 0..n_readers {
457 let tx = tx.clone();
458 thread::spawn(move || {
459 loop {
460 if let Some(msg) = ONCE_CELL.get() {
461 tx.send(msg).unwrap();
462 break;
463 }
464 #[cfg(target_env = "sgx")]
465 std::thread::yield_now();
466 }
467 });
468 }
469 for _ in 0..n_writers {
470 thread::spawn(move || {
471 let _ = ONCE_CELL.set(MSG.to_owned());
472 });
473 }
474
475 for _ in 0..n_readers {
476 let msg = rx.recv().unwrap();
477 assert_eq!(msg, MSG);
478 }
479 }
480
481 #[test]
482 fn dropck() {
483 let cell = OnceLock::new();
484 {
485 let s = String::new();
486 cell.set(&s).unwrap();
487 }
488 }
489}