oncelock/sync.rs
1//! Implements the [`OnceLock`] and [`LazyLock`] types.
2
3use crate::polyfill::{FinishNonExhaustive, UntaggedOptionCell};
4use core::cell::UnsafeCell;
5use core::fmt::{Debug, Formatter};
6use core::mem::ManuallyDrop;
7use core::ops::Deref;
8
9#[cfg(sync_backend = "parking_lot")]
10mod parking_lot;
11#[cfg(sync_backend = "parking_lot")]
12use self::parking_lot as backend;
13#[cfg(sync_backend = "std")]
14mod std_sync;
15#[cfg(sync_backend = "std")]
16use self::std_sync as backend;
17#[cfg(sync_backend = "spin")]
18mod spin;
19#[cfg(sync_backend = "spin")]
20use self::spin as backend;
21
22/// If this is `true`, then [`OnceLock::get_or_init`] may be poisoned on panic.
23///
24/// This is necessary when using versions of `std` before 1.51 or versions of `spin` before v0.10,
25/// which don't support [`std::sync::Once::call_once_force`].
26pub const LEGACY_POISON_BEHAVIOR: bool = backend::LEGACY_POISON_BEHAVIOR;
27
28/// A thread-safe value which is initialized at most once.
29///
30/// Emulates the [`std::sync::OnceLock`] type on old versions of rust.
31///
32/// Reading a non-None value out of `OnceCell` establishes a happens-before relationship with a corresponding write,
33/// just like use of the [`std::sync::Once::call_once`] method.
34pub struct OnceLock<T> {
35 once: backend::Once,
36 value: UntaggedOptionCell<T>,
37}
38impl<T> Default for OnceLock<T> {
39 #[inline]
40 fn default() -> Self {
41 Self::new()
42 }
43}
44impl<T> OnceLock<T> {
45 /// Create a new `OnceLock` with no associated data.
46 #[inline]
47 #[rustversion::attr(since(1.32), const)] // const UnsafeCell::new
48 pub fn new() -> Self {
49 OnceLock {
50 once: backend::Once::new(),
51 value: UntaggedOptionCell::uninit(),
52 }
53 }
54
55 /// Initializes the contents of this cell to the specified value.
56 ///
57 /// May block if initialization is currently in progress.
58 ///
59 /// # Errors
60 /// Returns an error containing the passed value if the cell is already initialized,
61 /// or `Ok(())` if the value
62 #[inline]
63 pub fn set(&self, value: T) -> Result<(), T> {
64 let mut value = Some(value);
65 self.get_or_init(|| value.take().unwrap());
66 if let Some(failed) = value {
67 Err(failed)
68 } else {
69 Ok(())
70 }
71 }
72
73 /// Get the reference to the underlying value,
74 /// triggering undefined behavior if uninitialized.
75 ///
76 /// # Safety
77 /// Caller is responsible for ensuring the data is fully initialized,
78 /// and for avoiding data races with concurrent initialization.
79 ///
80 /// # Panics
81 /// In debug mode, this may panic if uninitialized.
82 #[inline]
83 pub unsafe fn get_unchecked(&self) -> &T {
84 debug_assert!(self.get().is_some(), "UB: access uninitialized data");
85 // SAFETY: Guaranteed by caller
86 unsafe { &*self.value.get_ptr_unchecked() }
87 }
88
89 /// Get the value associated with this lock,
90 /// or `None` if uninitialized.
91 #[inline]
92 pub fn get(&self) -> Option<&T> {
93 if self.once.is_completed() {
94 // SAFETY: Guaranteed to be initialized
95 Some(unsafe { self.get_unchecked() })
96 } else {
97 None
98 }
99 }
100
101 /// Get the contents of the cell if initialized,
102 /// otherwise using the specified callback to initialize the value.
103 ///
104 /// Even in the presence of multiple threads, only one initialization function is ever invoked.
105 ///
106 /// # Panics
107 /// If the [`LEGACY_POISON_BEHAVIOR`] flag is false, initialization will be retried if a thread panics.
108 /// If [`LEGACY_POISON_BEHAVIOR`] is true, this will be poisoned if an initialization function panics.
109 /// In that case, all feature calls will unconditionally panic (just like [`std::sync::Once::call_once`]).
110 #[inline]
111 pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
112 self.get().unwrap_or_else(|| self.init_fallback(f))
113 }
114
115 #[cold]
116 fn init_fallback(&self, func: impl FnOnce() -> T) -> &T {
117 self.once.call_once_force(|| {
118 let value = func();
119 // SAFETY: Guaranteed to exclude all other threads,
120 // reads can only come after this `call_once` completes
121 unsafe {
122 self.value.write(value);
123 }
124 });
125 // SAFETY: Completion of call_once means we are initialized
126 unsafe { &*self.value.get_ptr_unchecked() }
127 }
128
129 /// Consumes the `OnceLock`, returning the wrapped value.
130 ///
131 /// Returns `None` if the cell was uninitialized.
132 #[inline]
133 pub fn into_inner(self) -> Option<T> {
134 let mut this = ManuallyDrop::new(self);
135 this.take()
136 }
137
138 /// Takes the value out of this `OnceLock`, moving it back to an uninitialized state.
139 ///
140 /// This is safe because it borrows the lock mutably.
141 #[inline]
142 pub fn take(&mut self) -> Option<T> {
143 if self.get().is_some() {
144 // A panic here would be UB
145 // Neither of these methods can possibly panic,
146 // but use a guard to make extra sure
147 let guard = AbortGuard;
148 struct AbortGuard;
149 impl Drop for AbortGuard {
150 fn drop(&mut self) {
151 // double panic due to abort
152 panic!("Panic here could cause invalid state to be seen");
153 }
154 }
155 // SAFETY: Value is guaranteed to be initialized because `get` returns Some
156 // Guard ensures that there is no panic before we reset our state
157 let value = unsafe { core::ptr::read(self.value.get_ptr_unchecked()) };
158 self.once = backend::Once::new();
159 core::mem::forget(guard);
160 Some(value)
161 } else {
162 None
163 }
164 }
165}
166impl<T> Drop for OnceLock<T> {
167 fn drop(&mut self) {
168 drop(self.take());
169 }
170}
171impl<T: Clone> Clone for OnceLock<T> {
172 fn clone(&self) -> Self {
173 match self.get() {
174 None => OnceLock::new(),
175 Some(inner) => OnceLock::from(inner.clone()),
176 }
177 }
178}
179// SAFETY: Valid since `T: Send`
180unsafe impl<T: Send> Send for OnceLock<T> {}
181// SAFETY: Valid since `T: Sync`, all mutation is otherwise synchronized
182unsafe impl<T: Sync> Sync for OnceLock<T> {}
183impl<T> From<T> for OnceLock<T> {
184 fn from(value: T) -> Self {
185 let res = OnceLock::new();
186 match res.set(value) {
187 Ok(()) => res,
188 Err(_) => {
189 // SAFETY: Not possible since we are the only user
190 unsafe { core::hint::unreachable_unchecked() }
191 }
192 }
193 }
194}
195
196enum LazyState<T, F> {
197 Initialized(T),
198 InProgress,
199 Uninitialized(F),
200}
201/// A lazily initialized value using a particular initialization function.
202///
203/// Unlike [`OnceLock`], this type is irrevocably poisoned if the initialization function panics.
204pub struct LazyLock<T, F = fn() -> T> {
205 once: backend::Once,
206 state: UnsafeCell<LazyState<T, F>>,
207}
208impl<T, F> LazyLock<T, F> {
209 /// Creates a new lazy value with the given initializing function.
210 ///
211 /// Logically, this should require [`F: FnOnce() -> T`](FnOnce),
212 /// but it cannot since that is not supported on the MSRV.
213 #[inline]
214 #[rustversion::attr(since(1.32), const)] // const UnsafeCell::new
215 pub fn new(func: F) -> Self {
216 LazyLock {
217 once: backend::Once::new(),
218 state: UnsafeCell::new(LazyState::Uninitialized(func)),
219 }
220 }
221
222 /// Get the value if the cell is initialized,
223 /// or `None` if not initialized yet.
224 #[inline]
225 pub fn get(this: &Self) -> Option<&T> {
226 if this.once.is_completed() {
227 // SAFETY: The `is_completed` method only returns true
228 // once initialization has finished.
229 Some(unsafe { Self::get_unchecked(this) })
230 } else {
231 None
232 }
233 }
234
235 #[inline]
236 unsafe fn get_unchecked(this: &Self) -> &T {
237 // SAFETY: Caller guarantees type is fully initialized,
238 // so not possible to observe these states
239 match unsafe { &*this.state.get() } {
240 LazyState::Uninitialized(_) | LazyState::InProgress => {
241 // SAFETY: Caller guarantees fully initialized, so not possible to encounter these
242 unsafe { core::hint::unreachable_unchecked() }
243 }
244 LazyState::Initialized(ref value) => value,
245 }
246 }
247}
248impl<T, F: FnOnce() -> T> LazyLock<T, F> {
249 /// Forces the evaluation of this lazy value and returns a reference to result.
250 /// This is equivalent to the Deref impl, but is explicit.
251 ///
252 /// This method will block the calling thread if another initialization routine is currently running.
253 ///
254 /// This function is implemented in terms of [`OnceLock::get_or_init`].
255 #[inline]
256 #[cfg_attr(has_track_caller, track_caller)]
257 pub fn force(this: &Self) -> &T {
258 Self::get(this).unwrap_or_else(|| Self::init_fallback(this))
259 }
260 #[cold]
261 #[cfg_attr(has_track_caller, track_caller)]
262 fn init_fallback(this: &Self) -> &T {
263 this.once.call_once(|| {
264 // SAFETY: Have exclusive access
265 // (recursive initialization will either deadlock or panic)
266 let this = unsafe { &mut *this.state.get() };
267 let func = match core::mem::replace(this, LazyState::InProgress) {
268 LazyState::Initialized(_) | LazyState::InProgress => unreachable!(),
269 LazyState::Uninitialized(func) => func,
270 };
271 *this = LazyState::Initialized(func());
272 });
273 // SAFETY: After call_once completes, we are fully initialized
274 unsafe { Self::get_unchecked(this) }
275 }
276}
277impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
278 type Target = T;
279
280 #[inline]
281 fn deref(&self) -> &Self::Target {
282 LazyLock::force(self)
283 }
284}
285
286impl<T: Debug, F> Debug for LazyLock<T, F> {
287 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
288 f.debug_struct("LazyLock")
289 .field("value", &Self::get(self))
290 .polyfill_finish_non_exhaustive()
291 }
292}