embassy_sync/
once_lock.rs1use core::cell::Cell;
4use core::future::{poll_fn, Future};
5use core::mem::MaybeUninit;
6use core::sync::atomic::{AtomicBool, Ordering};
7use core::task::Poll;
8
9pub struct OnceLock<T> {
41    init: AtomicBool,
42    data: Cell<MaybeUninit<T>>,
43}
44
45unsafe impl<T> Sync for OnceLock<T> {}
46
47impl<T> OnceLock<T> {
48    pub const fn new() -> Self {
50        Self {
51            init: AtomicBool::new(false),
52            data: Cell::new(MaybeUninit::zeroed()),
53        }
54    }
55
56    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    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    pub fn init(&self, value: T) -> Result<(), T> {
79        critical_section::with(|_| {
82            if !self.init.load(Ordering::Relaxed) {
84                self.data.set(MaybeUninit::new(value));
85                self.init.store(true, Ordering::Relaxed);
86                Ok(())
87
88            } else {
90                Err(value)
91            }
92        })
93    }
94
95    pub fn get_or_init<F>(&self, f: F) -> &T
97    where
98        F: FnOnce() -> T,
99    {
100        critical_section::with(|_| {
103            if !self.init.load(Ordering::Relaxed) {
105                self.data.set(MaybeUninit::new(f()));
106                self.init.store(true, Ordering::Relaxed);
107            }
108        });
109
110        unsafe { self.get_ref_unchecked() }
112    }
113
114    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    pub fn take(&mut self) -> Option<T> {
125        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            } else {
134                None
135            }
136        })
137    }
138
139    pub fn is_set(&self) -> bool {
141        self.init.load(Ordering::Relaxed)
142    }
143
144    unsafe fn get_ref_unchecked(&self) -> &T {
148        (*self.data.as_ptr()).assume_init_ref()
149    }
150}
151
152#[cfg(test)]
153mod 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}