| 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 |  | 
|---|