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