embassy_sync/
once_lock.rs1use core::cell::Cell;
4use core::fmt::{Debug, Formatter};
5use core::future::{poll_fn, Future};
6use core::mem::MaybeUninit;
7use core::sync::atomic::{AtomicBool, Ordering};
8use core::task::Poll;
9
10pub struct OnceLock<T> {
42 init: AtomicBool,
43 data: Cell<MaybeUninit<T>>,
44}
45
46impl<T> Debug for OnceLock<T> {
47 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
48 f.debug_struct("OnceLock")
49 .field("init", &self.init)
50 .field("data", &"Cell<MaybeUninit<{unprintable}>>")
51 .finish()
52 }
53}
54
55unsafe impl<T> Sync for OnceLock<T> where T: Sync {}
56
57impl<T> OnceLock<T> {
58 pub const fn new() -> Self {
60 Self {
61 init: AtomicBool::new(false),
62 data: Cell::new(MaybeUninit::zeroed()),
63 }
64 }
65
66 pub fn get(&self) -> impl Future<Output = &T> {
69 poll_fn(|cx| match self.try_get() {
70 Some(data) => Poll::Ready(data),
71 None => {
72 cx.waker().wake_by_ref();
73 Poll::Pending
74 }
75 })
76 }
77
78 pub fn try_get(&self) -> Option<&T> {
80 if self.init.load(Ordering::Relaxed) {
81 Some(unsafe { self.get_ref_unchecked() })
82 } else {
83 None
84 }
85 }
86
87 pub fn init(&self, value: T) -> Result<(), T> {
89 critical_section::with(|_| {
92 if !self.init.load(Ordering::Relaxed) {
94 self.data.set(MaybeUninit::new(value));
95 self.init.store(true, Ordering::Relaxed);
96 Ok(())
97
98 } else {
100 Err(value)
101 }
102 })
103 }
104
105 pub fn get_or_init<F>(&self, f: F) -> &T
107 where
108 F: FnOnce() -> T,
109 {
110 critical_section::with(|_| {
113 if !self.init.load(Ordering::Relaxed) {
115 self.data.set(MaybeUninit::new(f()));
116 self.init.store(true, Ordering::Relaxed);
117 }
118 });
119
120 unsafe { self.get_ref_unchecked() }
122 }
123
124 pub fn into_inner(self) -> Option<T> {
126 if self.init.load(Ordering::Relaxed) {
127 Some(unsafe { self.data.into_inner().assume_init() })
128 } else {
129 None
130 }
131 }
132
133 pub fn take(&mut self) -> Option<T> {
135 critical_section::with(|_| {
137 if self.init.load(Ordering::Relaxed) {
138 let val = unsafe { self.data.replace(MaybeUninit::zeroed()).assume_init() };
139 self.init.store(false, Ordering::Relaxed);
140 Some(val)
141
142 } else {
144 None
145 }
146 })
147 }
148
149 pub fn is_set(&self) -> bool {
151 self.init.load(Ordering::Relaxed)
152 }
153
154 unsafe fn get_ref_unchecked(&self) -> &T {
158 (*self.data.as_ptr()).assume_init_ref()
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn once_lock() {
168 let lock = OnceLock::new();
169 assert_eq!(lock.try_get(), None);
170 assert_eq!(lock.is_set(), false);
171
172 let v = 42;
173 assert_eq!(lock.init(v), Ok(()));
174 assert_eq!(lock.is_set(), true);
175 assert_eq!(lock.try_get(), Some(&v));
176 assert_eq!(lock.try_get(), Some(&v));
177
178 let v = 43;
179 assert_eq!(lock.init(v), Err(v));
180 assert_eq!(lock.is_set(), true);
181 assert_eq!(lock.try_get(), Some(&42));
182 }
183
184 #[test]
185 fn once_lock_get_or_init() {
186 let lock = OnceLock::new();
187 assert_eq!(lock.try_get(), None);
188 assert_eq!(lock.is_set(), false);
189
190 let v = lock.get_or_init(|| 42);
191 assert_eq!(v, &42);
192 assert_eq!(lock.is_set(), true);
193 assert_eq!(lock.try_get(), Some(&42));
194
195 let v = lock.get_or_init(|| 43);
196 assert_eq!(v, &42);
197 assert_eq!(lock.is_set(), true);
198 assert_eq!(lock.try_get(), Some(&42));
199 }
200
201 #[test]
202 fn once_lock_static() {
203 static LOCK: OnceLock<i32> = OnceLock::new();
204
205 let v: &'static i32 = LOCK.get_or_init(|| 42);
206 assert_eq!(v, &42);
207
208 let v: &'static i32 = LOCK.get_or_init(|| 43);
209 assert_eq!(v, &42);
210 }
211
212 #[futures_test::test]
213 async fn once_lock_async() {
214 static LOCK: OnceLock<i32> = OnceLock::new();
215
216 assert!(LOCK.init(42).is_ok());
217
218 let v: &'static i32 = LOCK.get().await;
219 assert_eq!(v, &42);
220 }
221
222 #[test]
223 fn once_lock_into_inner() {
224 let lock: OnceLock<i32> = OnceLock::new();
225
226 let v = lock.get_or_init(|| 42);
227 assert_eq!(v, &42);
228
229 assert_eq!(lock.into_inner(), Some(42));
230 }
231
232 #[test]
233 fn once_lock_take_init() {
234 let mut lock: OnceLock<i32> = OnceLock::new();
235
236 assert_eq!(lock.get_or_init(|| 42), &42);
237 assert_eq!(lock.is_set(), true);
238
239 assert_eq!(lock.take(), Some(42));
240 assert_eq!(lock.is_set(), false);
241
242 assert_eq!(lock.get_or_init(|| 43), &43);
243 assert_eq!(lock.is_set(), true);
244 }
245}