getrandom/
lazy.rs

1use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
2
3// This structure represents a lazily initialized static usize value. Useful
4// when it is preferable to just rerun initialization instead of locking.
5// Both unsync_init and sync_init will invoke an init() function until it
6// succeeds, then return the cached value for future calls.
7//
8// Both methods support init() "failing". If the init() method returns UNINIT,
9// that value will be returned as normal, but will not be cached.
10//
11// Users should only depend on the _value_ returned by init() functions.
12// Specifically, for the following init() function:
13//      fn init() -> usize {
14//          a();
15//          let v = b();
16//          c();
17//          v
18//      }
19// the effects of c() or writes to shared memory will not necessarily be
20// observed and additional synchronization methods with be needed.
21pub(crate) struct LazyUsize(AtomicUsize);
22
23impl LazyUsize {
24    pub const fn new() -> Self {
25        Self(AtomicUsize::new(Self::UNINIT))
26    }
27
28    // The initialization is not completed.
29    pub const UNINIT: usize = usize::max_value();
30
31    // Runs the init() function at least once, returning the value of some run
32    // of init(). Multiple callers can run their init() functions in parallel.
33    // init() should always return the same value, if it succeeds.
34    pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
35        // Relaxed ordering is fine, as we only have a single atomic variable.
36        let mut val = self.0.load(Relaxed);
37        if val == Self::UNINIT {
38            val = init();
39            self.0.store(val, Relaxed);
40        }
41        val
42    }
43}
44
45// Identical to LazyUsize except with bool instead of usize.
46pub(crate) struct LazyBool(LazyUsize);
47
48impl LazyBool {
49    pub const fn new() -> Self {
50        Self(LazyUsize::new())
51    }
52
53    pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
54        self.0.unsync_init(|| init() as usize) != 0
55    }
56}