1 // Copyright 2019 Developers of the Rand project.
2 //
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.
8 
9 use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
10 
11 // This structure represents a lazily initialized static usize value. Useful
12 // when it is preferable 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.
15 //
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.
18 //
19 // Users should only depend on the _value_ returned by init() functions.
20 // Specifically, for the following init() function:
21 //      fn init() -> usize {
22 //          a();
23 //          let v = b();
24 //          c();
25 //          v
26 //      }
27 // the effects of c() or writes to shared memory will not necessarily be
28 // observed and additional synchronization methods with be needed.
29 pub struct LazyUsize(AtomicUsize);
30 
31 impl LazyUsize {
new() -> Self32     pub const fn new() -> Self {
33         Self(AtomicUsize::new(Self::UNINIT))
34     }
35 
36     // The initialization is not completed.
37     pub const UNINIT: usize = usize::max_value();
38 
39     // Runs the init() function at least once, returning the value of some run
40     // of init(). Multiple callers can run their init() functions in parallel.
41     // init() should always return the same value, if it succeeds.
unsync_init(&self, init: impl FnOnce() -> usize) -> usize42     pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
43         // Relaxed ordering is fine, as we only have a single atomic variable.
44         let mut val = self.0.load(Relaxed);
45         if val == Self::UNINIT {
46             val = init();
47             self.0.store(val, Relaxed);
48         }
49         val
50     }
51 }
52 
53 // Identical to LazyUsize except with bool instead of usize.
54 pub struct LazyBool(LazyUsize);
55 
56 impl LazyBool {
new() -> Self57     pub const fn new() -> Self {
58         Self(LazyUsize::new())
59     }
60 
unsync_init(&self, init: impl FnOnce() -> bool) -> bool61     pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
62         self.0.unsync_init(|| init() as usize) != 0
63     }
64 }
65