1//! Synchronization primitive for initializing a value once, allowing others to get a reference to the value.
23use core::cell::UnsafeCell;
4use core::mem::ManuallyDrop;
5use core::sync::atomic::{AtomicBool, Ordering};
67/// The `LazyLock` is a synchronization primitive that allows for
8/// initializing a value once, and allowing others to obtain a
9/// reference to the value. This is useful for lazy initialization of
10/// a static value.
11///
12/// # Example
13/// ```
14/// use futures_executor::block_on;
15/// use embassy_sync::lazy_lock::LazyLock;
16///
17/// // Define a static value that will be lazily initialized
18/// // at runtime at the first access.
19/// static VALUE: LazyLock<u32> = LazyLock::new(|| 20);
20///
21/// let reference = VALUE.get();
22/// assert_eq!(reference, &20);
23/// ```
24pub struct LazyLock<T, F = fn() -> T> {
25 init: AtomicBool,
26 data: UnsafeCell<Data<T, F>>,
27}
2829union Data<T, F> {
30 value: ManuallyDrop<T>,
31 f: ManuallyDrop<F>,
32}
3334unsafe impl<T, F> Sync for LazyLock<T, F> {}
3536impl<T, F: FnOnce() -> T> LazyLock<T, F> {
37/// Create a new uninitialized `StaticLock`.
38pub const fn new(init_fn: F) -> Self {
39Self {
40 init: AtomicBool::new(false),
41 data: UnsafeCell::new(Data {
42 f: ManuallyDrop::new(init_fn),
43 }),
44 }
45 }
4647/// Get a reference to the underlying value, initializing it if it
48 /// has not been done already.
49#[inline]
50pub fn get(&self) -> &T {
51self.ensure_init_fast();
52unsafe { &(*self.data.get()).value }
53 }
5455/// Consume the `LazyLock`, returning the underlying value. The
56 /// initialization function will be called if it has not been
57 /// already.
58#[inline]
59pub fn into_inner(self) -> T {
60self.ensure_init_fast();
61let this = ManuallyDrop::new(self);
62let data = unsafe { core::ptr::read(&this.data) }.into_inner();
6364 ManuallyDrop::into_inner(unsafe { data.value })
65 }
6667/// Initialize the `LazyLock` if it has not been initialized yet.
68 /// This function is a fast track to [`Self::ensure_init`]
69 /// which does not require a critical section in most cases when
70 /// the value has been initialized already.
71 /// When this function returns, `self.data` is guaranteed to be
72 /// initialized and visible on the current core.
73#[inline]
74fn ensure_init_fast(&self) {
75if !self.init.load(Ordering::Acquire) {
76self.ensure_init();
77 }
78 }
7980/// Initialize the `LazyLock` if it has not been initialized yet.
81 /// When this function returns, `self.data` is guaranteed to be
82 /// initialized and visible on the current core.
83fn ensure_init(&self) {
84 critical_section::with(|_| {
85if !self.init.load(Ordering::Acquire) {
86let data = unsafe { &mut *self.data.get() };
87let f = unsafe { ManuallyDrop::take(&mut data.f) };
88let value = f();
89 data.value = ManuallyDrop::new(value);
9091self.init.store(true, Ordering::Release);
92 }
93 });
94 }
95}
9697impl<T, F> Drop for LazyLock<T, F> {
98fn drop(&mut self) {
99if self.init.load(Ordering::Acquire) {
100unsafe { ManuallyDrop::drop(&mut self.data.get_mut().value) };
101 } else {
102unsafe { ManuallyDrop::drop(&mut self.data.get_mut().f) };
103 }
104 }
105}
106107#[cfg(test)]
108mod tests {
109use core::sync::atomic::{AtomicU32, Ordering};
110111use super::*;
112113#[test]
114fn test_lazy_lock() {
115static VALUE: LazyLock<u32> = LazyLock::new(|| 20);
116let reference = VALUE.get();
117assert_eq!(reference, &20);
118 }
119#[test]
120fn test_lazy_lock_into_inner() {
121let lazy: LazyLock<u32> = LazyLock::new(|| 20);
122let value = lazy.into_inner();
123assert_eq!(value, 20);
124 }
125126static DROP_CHECKER: AtomicU32 = AtomicU32::new(0);
127struct DropCheck;
128129impl Drop for DropCheck {
130fn drop(&mut self) {
131 DROP_CHECKER.fetch_add(1, Ordering::Acquire);
132 }
133 }
134135#[test]
136fn test_lazy_drop() {
137let lazy: LazyLock<DropCheck> = LazyLock::new(|| DropCheck);
138assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 0);
139 lazy.get();
140 drop(lazy);
141assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 1);
142143let dropper = DropCheck;
144let lazy_fn: LazyLock<u32, _> = LazyLock::new(move || {
145let _a = dropper;
14620
147});
148assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 1);
149 drop(lazy_fn);
150assert_eq!(DROP_CHECKER.load(Ordering::Acquire), 2);
151 }
152}