| 1 | use 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 | // unsync_init will invoke an init() function until it succeeds, then return the |
| 6 | // cached value for future calls. |
| 7 | // |
| 8 | // unsync_init supports 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 may be needed. |
| 21 | pub(crate) struct LazyUsize(AtomicUsize); |
| 22 | |
| 23 | impl 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 most once, returning the value of some run of |
| 32 | // 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: usize = self.0.load(order:Relaxed); |
| 37 | if val == Self::UNINIT { |
| 38 | val = init(); |
| 39 | self.0.store(val, order:Relaxed); |
| 40 | } |
| 41 | val |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | // Identical to LazyUsize except with bool instead of usize. |
| 46 | pub(crate) struct LazyBool(LazyUsize); |
| 47 | |
| 48 | impl 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 | } |
| 57 | |