1 // Copyright 2019 Developers of the Rand project.
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
9 use core
::sync
::atomic
::{AtomicUsize, Ordering::Relaxed}
;
11 // This structure represents a laziliy initialized static usize value. Useful
12 // when it is perferable to just rerun initialization instead of locking.
13 // Both unsync_init and sync_init will invoke an init() function until it
14 // succeeds, then return the cached value for future calls.
16 // Both methods support init() "failing". If the init() method returns UNINIT,
17 // that value will be returned as normal, but will not be cached.
19 // Users should only depend on the _value_ returned by init() functions.
20 // Specifically, for the following init() function:
21 // fn init() -> usize {
27 // the effects of c() or writes to shared memory will not necessarily be
28 // observed and additional syncronization methods with be needed.
29 pub struct LazyUsize(AtomicUsize
);
32 pub const fn new() -> Self {
33 Self(AtomicUsize
::new(Self::UNINIT
))
36 // The initialization is not completed.
37 pub const UNINIT
: usize = usize::max_value();
38 // The initialization is currently running.
39 pub const ACTIVE
: usize = usize::max_value() - 1;
41 // Runs the init() function at least once, returning the value of some run
42 // of init(). Multiple callers can run their init() functions in parallel.
43 // init() should always return the same value, if it succeeds.
44 pub fn unsync_init(&self, init
: impl FnOnce() -> usize) -> usize {
45 // Relaxed ordering is fine, as we only have a single atomic variable.
46 let mut val
= self.0.load(Relaxed
);
47 if val
== Self::UNINIT
{
49 self.0.store(val
, Relaxed
);
54 // Synchronously runs the init() function. Only one caller will have their
55 // init() function running at a time, and exactly one successful call will
56 // be run. init() returning UNINIT or ACTIVE will be considered a failure,
57 // and future calls to sync_init will rerun their init() function.
58 pub fn sync_init(&self, init
: impl FnOnce() -> usize, mut wait
: impl FnMut()) -> usize {
59 // Common and fast path with no contention. Don't wast time on CAS.
60 match self.0.load(Relaxed
) {
61 Self::UNINIT
| Self::ACTIVE
=> {}
64 // Relaxed ordering is fine, as we only have a single atomic variable.
66 match self.0.compare_and_swap(Self::UNINIT
, Self::ACTIVE
, Relaxed
) {
71 Self::UNINIT
| Self::ACTIVE
=> Self::UNINIT
,
78 Self::ACTIVE
=> wait(),
85 // Identical to LazyUsize except with bool instead of usize.
86 pub struct LazyBool(LazyUsize
);
89 pub const fn new() -> Self {
90 Self(LazyUsize
::new())
93 pub fn unsync_init(&self, init
: impl FnOnce() -> bool
) -> bool
{
94 self.0.unsync_init
(|| init() as usize) != 0