parking_lot/once.rs
1// Copyright 2016 Amanieu d'Antras
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 crate::util::UncheckedOptionExt;
9use core::{
10 fmt, mem,
11 sync::atomic::{fence, AtomicU8, Ordering},
12};
13use parking_lot_core::{self, SpinWait, DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN};
14
15const DONE_BIT: u8 = 1;
16const POISON_BIT: u8 = 2;
17const LOCKED_BIT: u8 = 4;
18const PARKED_BIT: u8 = 8;
19
20/// Current state of a `Once`.
21#[derive(Copy, Clone, Eq, PartialEq, Debug)]
22pub enum OnceState {
23 /// A closure has not been executed yet
24 New,
25
26 /// A closure was executed but panicked.
27 Poisoned,
28
29 /// A thread is currently executing a closure.
30 InProgress,
31
32 /// A closure has completed successfully.
33 Done,
34}
35
36impl OnceState {
37 /// Returns whether the associated `Once` has been poisoned.
38 ///
39 /// Once an initialization routine for a `Once` has panicked it will forever
40 /// indicate to future forced initialization routines that it is poisoned.
41 #[inline]
42 pub fn poisoned(self) -> bool {
43 matches!(self, OnceState::Poisoned)
44 }
45
46 /// Returns whether the associated `Once` has successfully executed a
47 /// closure.
48 #[inline]
49 pub fn done(self) -> bool {
50 matches!(self, OnceState::Done)
51 }
52}
53
54/// A synchronization primitive which can be used to run a one-time
55/// initialization. Useful for one-time initialization for globals, FFI or
56/// related functionality.
57///
58/// # Differences from the standard library `Once`
59///
60/// - Only requires 1 byte of space, instead of 1 word.
61/// - Not required to be `'static`.
62/// - Relaxed memory barriers in the fast path, which can significantly improve
63/// performance on some architectures.
64/// - Efficient handling of micro-contention using adaptive spinning.
65///
66/// # Examples
67///
68/// ```
69/// use parking_lot::Once;
70///
71/// static START: Once = Once::new();
72///
73/// START.call_once(|| {
74/// // run initialization here
75/// });
76/// ```
77pub struct Once(AtomicU8);
78
79impl Once {
80 /// Creates a new `Once` value.
81 #[inline]
82 pub const fn new() -> Once {
83 Once(AtomicU8::new(0))
84 }
85
86 /// Creates a new `Once` value that is already in the completed state.
87 ///
88 /// A `Once` created this way will never invoke a closure passed to
89 /// [`call_once`](Self::call_once) or [`call_once_force`](Self::call_once_force),
90 /// and its [`state`](Self::state) will always be [`OnceState::Done`].
91 ///
92 /// # Examples
93 ///
94 /// ```
95 /// use parking_lot::{Once, OnceState};
96 ///
97 /// static INIT: Once = Once::new_completed();
98 /// assert_eq!(INIT.state(), OnceState::Done);
99 ///
100 /// // The closure is never executed.
101 /// INIT.call_once(|| unreachable!());
102 /// ```
103 #[inline]
104 pub const fn new_completed() -> Once {
105 Once(AtomicU8::new(DONE_BIT))
106 }
107
108 /// Returns the current state of this `Once`.
109 #[inline]
110 pub fn state(&self) -> OnceState {
111 let state = self.0.load(Ordering::Acquire);
112 if state & DONE_BIT != 0 {
113 OnceState::Done
114 } else if state & LOCKED_BIT != 0 {
115 OnceState::InProgress
116 } else if state & POISON_BIT != 0 {
117 OnceState::Poisoned
118 } else {
119 OnceState::New
120 }
121 }
122
123 /// Performs an initialization routine once and only once. The given closure
124 /// will be executed if this is the first time `call_once` has been called,
125 /// and otherwise the routine will *not* be invoked.
126 ///
127 /// This method will block the calling thread if another initialization
128 /// routine is currently running.
129 ///
130 /// When this function returns, it is guaranteed that some initialization
131 /// has run and completed (it may not be the closure specified). It is also
132 /// guaranteed that any memory writes performed by the executed closure can
133 /// be reliably observed by other threads at this point (there is a
134 /// happens-before relation between the closure and code executing after the
135 /// return).
136 ///
137 /// # Examples
138 ///
139 /// ```
140 /// use parking_lot::Once;
141 ///
142 /// static mut VAL: usize = 0;
143 /// static INIT: Once = Once::new();
144 ///
145 /// // Accessing a `static mut` is unsafe much of the time, but if we do so
146 /// // in a synchronized fashion (e.g. write once or read all) then we're
147 /// // good to go!
148 /// //
149 /// // This function will only call `expensive_computation` once, and will
150 /// // otherwise always return the value returned from the first invocation.
151 /// fn get_cached_val() -> usize {
152 /// unsafe {
153 /// INIT.call_once(|| {
154 /// VAL = expensive_computation();
155 /// });
156 /// VAL
157 /// }
158 /// }
159 ///
160 /// fn expensive_computation() -> usize {
161 /// // ...
162 /// # 2
163 /// }
164 /// ```
165 ///
166 /// # Panics
167 ///
168 /// The closure `f` will only be executed once if this is called
169 /// concurrently amongst many threads. If that closure panics, however, then
170 /// it will *poison* this `Once` instance, causing all future invocations of
171 /// `call_once` to also panic.
172 #[inline]
173 pub fn call_once<F>(&self, f: F)
174 where
175 F: FnOnce(),
176 {
177 if self.0.load(Ordering::Acquire) == DONE_BIT {
178 return;
179 }
180
181 let mut f = Some(f);
182 self.call_once_slow(false, &mut |_| unsafe { f.take().unchecked_unwrap()() });
183 }
184
185 /// Performs the same function as `call_once` except ignores poisoning.
186 ///
187 /// If this `Once` has been poisoned (some initialization panicked) then
188 /// this function will continue to attempt to call initialization functions
189 /// until one of them doesn't panic.
190 ///
191 /// The closure `f` is yielded a structure which can be used to query the
192 /// state of this `Once` (whether initialization has previously panicked or
193 /// not).
194 #[inline]
195 pub fn call_once_force<F>(&self, f: F)
196 where
197 F: FnOnce(OnceState),
198 {
199 if self.0.load(Ordering::Acquire) == DONE_BIT {
200 return;
201 }
202
203 let mut f = Some(f);
204 self.call_once_slow(true, &mut |state| unsafe {
205 f.take().unchecked_unwrap()(state)
206 });
207 }
208
209 // This is a non-generic function to reduce the monomorphization cost of
210 // using `call_once` (this isn't exactly a trivial or small implementation).
211 //
212 // Additionally, this is tagged with `#[cold]` as it should indeed be cold
213 // and it helps let LLVM know that calls to this function should be off the
214 // fast path. Essentially, this should help generate more straight line code
215 // in LLVM.
216 //
217 // Finally, this takes an `FnMut` instead of a `FnOnce` because there's
218 // currently no way to take an `FnOnce` and call it via virtual dispatch
219 // without some allocation overhead.
220 #[cold]
221 fn call_once_slow(&self, ignore_poison: bool, f: &mut dyn FnMut(OnceState)) {
222 let mut spinwait = SpinWait::new();
223 let mut state = self.0.load(Ordering::Relaxed);
224 loop {
225 // If another thread called the closure, we're done
226 if state & DONE_BIT != 0 {
227 // An acquire fence is needed here since we didn't load the
228 // state with Ordering::Acquire.
229 fence(Ordering::Acquire);
230 return;
231 }
232
233 // If the state has been poisoned and we aren't forcing, then panic
234 if state & POISON_BIT != 0 && !ignore_poison {
235 // Need the fence here as well for the same reason
236 fence(Ordering::Acquire);
237 panic!("Once instance has previously been poisoned");
238 }
239
240 // Grab the lock if it isn't locked, even if there is a queue on it.
241 // We also clear the poison bit since we are going to try running
242 // the closure again.
243 if state & LOCKED_BIT == 0 {
244 match self.0.compare_exchange_weak(
245 state,
246 (state | LOCKED_BIT) & !POISON_BIT,
247 Ordering::Acquire,
248 Ordering::Relaxed,
249 ) {
250 Ok(_) => break,
251 Err(x) => state = x,
252 }
253 continue;
254 }
255
256 // If there is no queue, try spinning a few times
257 if state & PARKED_BIT == 0 && spinwait.spin() {
258 state = self.0.load(Ordering::Relaxed);
259 continue;
260 }
261
262 // Set the parked bit
263 if state & PARKED_BIT == 0 {
264 if let Err(x) = self.0.compare_exchange_weak(
265 state,
266 state | PARKED_BIT,
267 Ordering::Relaxed,
268 Ordering::Relaxed,
269 ) {
270 state = x;
271 continue;
272 }
273 }
274
275 // Park our thread until we are woken up by the thread that owns the
276 // lock.
277 let addr = self as *const _ as usize;
278 let validate = || self.0.load(Ordering::Relaxed) == LOCKED_BIT | PARKED_BIT;
279 let before_sleep = || {};
280 let timed_out = |_, _| unreachable!();
281 unsafe {
282 parking_lot_core::park(
283 addr,
284 validate,
285 before_sleep,
286 timed_out,
287 DEFAULT_PARK_TOKEN,
288 None,
289 );
290 }
291
292 // Loop back and check if the done bit was set
293 spinwait.reset();
294 state = self.0.load(Ordering::Relaxed);
295 }
296
297 struct PanicGuard<'a>(&'a Once);
298 impl<'a> Drop for PanicGuard<'a> {
299 fn drop(&mut self) {
300 // Mark the state as poisoned, unlock it and unpark all threads.
301 let once = self.0;
302 let state = once.0.swap(POISON_BIT, Ordering::Release);
303 if state & PARKED_BIT != 0 {
304 let addr = once as *const _ as usize;
305 unsafe {
306 parking_lot_core::unpark_all(addr, DEFAULT_UNPARK_TOKEN);
307 }
308 }
309 }
310 }
311
312 // At this point we have the lock, so run the closure. Make sure we
313 // properly clean up if the closure panicks.
314 let guard = PanicGuard(self);
315 let once_state = if state & POISON_BIT != 0 {
316 OnceState::Poisoned
317 } else {
318 OnceState::New
319 };
320 f(once_state);
321 mem::forget(guard);
322
323 // Now unlock the state, set the done bit and unpark all threads
324 let state = self.0.swap(DONE_BIT, Ordering::Release);
325 if state & PARKED_BIT != 0 {
326 let addr = self as *const _ as usize;
327 unsafe {
328 parking_lot_core::unpark_all(addr, DEFAULT_UNPARK_TOKEN);
329 }
330 }
331 }
332}
333
334impl Default for Once {
335 #[inline]
336 fn default() -> Once {
337 Once::new()
338 }
339}
340
341impl fmt::Debug for Once {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 f.debug_struct("Once")
344 .field("state", &self.state())
345 .finish()
346 }
347}
348
349#[cfg(test)]
350mod tests {
351 use crate::Once;
352 use std::panic;
353 use std::sync::mpsc::channel;
354 use std::thread;
355
356 #[test]
357 fn smoke_once() {
358 static O: Once = Once::new();
359 let mut a = 0;
360 O.call_once(|| a += 1);
361 assert_eq!(a, 1);
362 O.call_once(|| a += 1);
363 assert_eq!(a, 1);
364 }
365
366 #[test]
367 fn stampede_once() {
368 static O: Once = Once::new();
369 static mut RUN: bool = false;
370
371 let (tx, rx) = channel();
372 for _ in 0..10 {
373 let tx = tx.clone();
374 thread::spawn(move || {
375 for _ in 0..4 {
376 thread::yield_now()
377 }
378 unsafe {
379 O.call_once(|| {
380 assert!(!RUN);
381 RUN = true;
382 });
383 assert!(RUN);
384 }
385 tx.send(()).unwrap();
386 });
387 }
388
389 unsafe {
390 O.call_once(|| {
391 assert!(!RUN);
392 RUN = true;
393 });
394 assert!(RUN);
395 }
396
397 for _ in 0..10 {
398 rx.recv().unwrap();
399 }
400 }
401
402 #[test]
403 fn poison_bad() {
404 static O: Once = Once::new();
405
406 // poison the once
407 let t = panic::catch_unwind(|| {
408 O.call_once(|| panic!());
409 });
410 assert!(t.is_err());
411
412 // poisoning propagates
413 let t = panic::catch_unwind(|| {
414 O.call_once(|| {});
415 });
416 assert!(t.is_err());
417
418 // we can subvert poisoning, however
419 let mut called = false;
420 O.call_once_force(|p| {
421 called = true;
422 assert!(p.poisoned())
423 });
424 assert!(called);
425
426 // once any success happens, we stop propagating the poison
427 O.call_once(|| {});
428 }
429
430 #[test]
431 fn wait_for_force_to_finish() {
432 static O: Once = Once::new();
433
434 // poison the once
435 let t = panic::catch_unwind(|| {
436 O.call_once(|| panic!());
437 });
438 assert!(t.is_err());
439
440 // make sure someone's waiting inside the once via a force
441 let (tx1, rx1) = channel();
442 let (tx2, rx2) = channel();
443 let t1 = thread::spawn(move || {
444 O.call_once_force(|p| {
445 assert!(p.poisoned());
446 tx1.send(()).unwrap();
447 rx2.recv().unwrap();
448 });
449 });
450
451 rx1.recv().unwrap();
452
453 // put another waiter on the once
454 let t2 = thread::spawn(|| {
455 let mut called = false;
456 O.call_once(|| {
457 called = true;
458 });
459 assert!(!called);
460 });
461
462 tx2.send(()).unwrap();
463
464 assert!(t1.join().is_ok());
465 assert!(t2.join().is_ok());
466 }
467
468 #[test]
469 fn test_once_debug() {
470 static O: Once = Once::new();
471
472 assert_eq!(format!("{:?}", O), "Once { state: New }");
473 }
474
475 #[test]
476 fn new_completed_is_done() {
477 use crate::OnceState;
478
479 static O: Once = Once::new_completed();
480 assert_eq!(O.state(), OnceState::Done);
481 assert!(O.state().done());
482 assert!(!O.state().poisoned());
483
484 let mut called = false;
485 O.call_once(|| called = true);
486 assert!(!called);
487 assert_eq!(O.state(), OnceState::Done);
488 }
489
490 #[test]
491 fn new_default_is_new() {
492 // Sanity check that the default constructor is unchanged.
493 static O: Once = Once::new();
494 let mut called = false;
495 O.call_once(|| called = true);
496 assert!(called);
497 }
498}