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    /// Returns the current state of this `Once`.
87    #[inline]
88    pub fn state(&self) -> OnceState {
89        let state = self.0.load(Ordering::Acquire);
90        if state & DONE_BIT != 0 {
91            OnceState::Done
92        } else if state & LOCKED_BIT != 0 {
93            OnceState::InProgress
94        } else if state & POISON_BIT != 0 {
95            OnceState::Poisoned
96        } else {
97            OnceState::New
98        }
99    }
100
101    /// Performs an initialization routine once and only once. The given closure
102    /// will be executed if this is the first time `call_once` has been called,
103    /// and otherwise the routine will *not* be invoked.
104    ///
105    /// This method will block the calling thread if another initialization
106    /// routine is currently running.
107    ///
108    /// When this function returns, it is guaranteed that some initialization
109    /// has run and completed (it may not be the closure specified). It is also
110    /// guaranteed that any memory writes performed by the executed closure can
111    /// be reliably observed by other threads at this point (there is a
112    /// happens-before relation between the closure and code executing after the
113    /// return).
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use parking_lot::Once;
119    ///
120    /// static mut VAL: usize = 0;
121    /// static INIT: Once = Once::new();
122    ///
123    /// // Accessing a `static mut` is unsafe much of the time, but if we do so
124    /// // in a synchronized fashion (e.g. write once or read all) then we're
125    /// // good to go!
126    /// //
127    /// // This function will only call `expensive_computation` once, and will
128    /// // otherwise always return the value returned from the first invocation.
129    /// fn get_cached_val() -> usize {
130    ///     unsafe {
131    ///         INIT.call_once(|| {
132    ///             VAL = expensive_computation();
133    ///         });
134    ///         VAL
135    ///     }
136    /// }
137    ///
138    /// fn expensive_computation() -> usize {
139    ///     // ...
140    /// # 2
141    /// }
142    /// ```
143    ///
144    /// # Panics
145    ///
146    /// The closure `f` will only be executed once if this is called
147    /// concurrently amongst many threads. If that closure panics, however, then
148    /// it will *poison* this `Once` instance, causing all future invocations of
149    /// `call_once` to also panic.
150    #[inline]
151    pub fn call_once<F>(&self, f: F)
152    where
153        F: FnOnce(),
154    {
155        if self.0.load(Ordering::Acquire) == DONE_BIT {
156            return;
157        }
158
159        let mut f = Some(f);
160        self.call_once_slow(false, &mut |_| unsafe { f.take().unchecked_unwrap()() });
161    }
162
163    /// Performs the same function as `call_once` except ignores poisoning.
164    ///
165    /// If this `Once` has been poisoned (some initialization panicked) then
166    /// this function will continue to attempt to call initialization functions
167    /// until one of them doesn't panic.
168    ///
169    /// The closure `f` is yielded a structure which can be used to query the
170    /// state of this `Once` (whether initialization has previously panicked or
171    /// not).
172    #[inline]
173    pub fn call_once_force<F>(&self, f: F)
174    where
175        F: FnOnce(OnceState),
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(true, &mut |state| unsafe {
183            f.take().unchecked_unwrap()(state)
184        });
185    }
186
187    // This is a non-generic function to reduce the monomorphization cost of
188    // using `call_once` (this isn't exactly a trivial or small implementation).
189    //
190    // Additionally, this is tagged with `#[cold]` as it should indeed be cold
191    // and it helps let LLVM know that calls to this function should be off the
192    // fast path. Essentially, this should help generate more straight line code
193    // in LLVM.
194    //
195    // Finally, this takes an `FnMut` instead of a `FnOnce` because there's
196    // currently no way to take an `FnOnce` and call it via virtual dispatch
197    // without some allocation overhead.
198    #[cold]
199    fn call_once_slow(&self, ignore_poison: bool, f: &mut dyn FnMut(OnceState)) {
200        let mut spinwait = SpinWait::new();
201        let mut state = self.0.load(Ordering::Relaxed);
202        loop {
203            // If another thread called the closure, we're done
204            if state & DONE_BIT != 0 {
205                // An acquire fence is needed here since we didn't load the
206                // state with Ordering::Acquire.
207                fence(Ordering::Acquire);
208                return;
209            }
210
211            // If the state has been poisoned and we aren't forcing, then panic
212            if state & POISON_BIT != 0 && !ignore_poison {
213                // Need the fence here as well for the same reason
214                fence(Ordering::Acquire);
215                panic!("Once instance has previously been poisoned");
216            }
217
218            // Grab the lock if it isn't locked, even if there is a queue on it.
219            // We also clear the poison bit since we are going to try running
220            // the closure again.
221            if state & LOCKED_BIT == 0 {
222                match self.0.compare_exchange_weak(
223                    state,
224                    (state | LOCKED_BIT) & !POISON_BIT,
225                    Ordering::Acquire,
226                    Ordering::Relaxed,
227                ) {
228                    Ok(_) => break,
229                    Err(x) => state = x,
230                }
231                continue;
232            }
233
234            // If there is no queue, try spinning a few times
235            if state & PARKED_BIT == 0 && spinwait.spin() {
236                state = self.0.load(Ordering::Relaxed);
237                continue;
238            }
239
240            // Set the parked bit
241            if state & PARKED_BIT == 0 {
242                if let Err(x) = self.0.compare_exchange_weak(
243                    state,
244                    state | PARKED_BIT,
245                    Ordering::Relaxed,
246                    Ordering::Relaxed,
247                ) {
248                    state = x;
249                    continue;
250                }
251            }
252
253            // Park our thread until we are woken up by the thread that owns the
254            // lock.
255            let addr = self as *const _ as usize;
256            let validate = || self.0.load(Ordering::Relaxed) == LOCKED_BIT | PARKED_BIT;
257            let before_sleep = || {};
258            let timed_out = |_, _| unreachable!();
259            unsafe {
260                parking_lot_core::park(
261                    addr,
262                    validate,
263                    before_sleep,
264                    timed_out,
265                    DEFAULT_PARK_TOKEN,
266                    None,
267                );
268            }
269
270            // Loop back and check if the done bit was set
271            spinwait.reset();
272            state = self.0.load(Ordering::Relaxed);
273        }
274
275        struct PanicGuard<'a>(&'a Once);
276        impl<'a> Drop for PanicGuard<'a> {
277            fn drop(&mut self) {
278                // Mark the state as poisoned, unlock it and unpark all threads.
279                let once = self.0;
280                let state = once.0.swap(POISON_BIT, Ordering::Release);
281                if state & PARKED_BIT != 0 {
282                    let addr = once as *const _ as usize;
283                    unsafe {
284                        parking_lot_core::unpark_all(addr, DEFAULT_UNPARK_TOKEN);
285                    }
286                }
287            }
288        }
289
290        // At this point we have the lock, so run the closure. Make sure we
291        // properly clean up if the closure panicks.
292        let guard = PanicGuard(self);
293        let once_state = if state & POISON_BIT != 0 {
294            OnceState::Poisoned
295        } else {
296            OnceState::New
297        };
298        f(once_state);
299        mem::forget(guard);
300
301        // Now unlock the state, set the done bit and unpark all threads
302        let state = self.0.swap(DONE_BIT, Ordering::Release);
303        if state & PARKED_BIT != 0 {
304            let addr = self as *const _ as usize;
305            unsafe {
306                parking_lot_core::unpark_all(addr, DEFAULT_UNPARK_TOKEN);
307            }
308        }
309    }
310}
311
312impl Default for Once {
313    #[inline]
314    fn default() -> Once {
315        Once::new()
316    }
317}
318
319impl fmt::Debug for Once {
320    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321        f.debug_struct("Once")
322            .field("state", &self.state())
323            .finish()
324    }
325}
326
327#[cfg(test)]
328mod tests {
329    use crate::Once;
330    use std::panic;
331    use std::sync::mpsc::channel;
332    use std::thread;
333
334    #[test]
335    fn smoke_once() {
336        static O: Once = Once::new();
337        let mut a = 0;
338        O.call_once(|| a += 1);
339        assert_eq!(a, 1);
340        O.call_once(|| a += 1);
341        assert_eq!(a, 1);
342    }
343
344    #[test]
345    fn stampede_once() {
346        static O: Once = Once::new();
347        static mut RUN: bool = false;
348
349        let (tx, rx) = channel();
350        for _ in 0..10 {
351            let tx = tx.clone();
352            thread::spawn(move || {
353                for _ in 0..4 {
354                    thread::yield_now()
355                }
356                unsafe {
357                    O.call_once(|| {
358                        assert!(!RUN);
359                        RUN = true;
360                    });
361                    assert!(RUN);
362                }
363                tx.send(()).unwrap();
364            });
365        }
366
367        unsafe {
368            O.call_once(|| {
369                assert!(!RUN);
370                RUN = true;
371            });
372            assert!(RUN);
373        }
374
375        for _ in 0..10 {
376            rx.recv().unwrap();
377        }
378    }
379
380    #[test]
381    fn poison_bad() {
382        static O: Once = Once::new();
383
384        // poison the once
385        let t = panic::catch_unwind(|| {
386            O.call_once(|| panic!());
387        });
388        assert!(t.is_err());
389
390        // poisoning propagates
391        let t = panic::catch_unwind(|| {
392            O.call_once(|| {});
393        });
394        assert!(t.is_err());
395
396        // we can subvert poisoning, however
397        let mut called = false;
398        O.call_once_force(|p| {
399            called = true;
400            assert!(p.poisoned())
401        });
402        assert!(called);
403
404        // once any success happens, we stop propagating the poison
405        O.call_once(|| {});
406    }
407
408    #[test]
409    fn wait_for_force_to_finish() {
410        static O: Once = Once::new();
411
412        // poison the once
413        let t = panic::catch_unwind(|| {
414            O.call_once(|| panic!());
415        });
416        assert!(t.is_err());
417
418        // make sure someone's waiting inside the once via a force
419        let (tx1, rx1) = channel();
420        let (tx2, rx2) = channel();
421        let t1 = thread::spawn(move || {
422            O.call_once_force(|p| {
423                assert!(p.poisoned());
424                tx1.send(()).unwrap();
425                rx2.recv().unwrap();
426            });
427        });
428
429        rx1.recv().unwrap();
430
431        // put another waiter on the once
432        let t2 = thread::spawn(|| {
433            let mut called = false;
434            O.call_once(|| {
435                called = true;
436            });
437            assert!(!called);
438        });
439
440        tx2.send(()).unwrap();
441
442        assert!(t1.join().is_ok());
443        assert!(t2.join().is_ok());
444    }
445
446    #[test]
447    fn test_once_debug() {
448        static O: Once = Once::new();
449
450        assert_eq!(format!("{:?}", O), "Once { state: New }");
451    }
452}