blob: 06e23c28c504553b469236aef4f40372b7a94839 [file] [log] [blame]
Joel Galenson4be0c6d2020-07-07 13:20:14 -07001// 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.
ThiƩbaud Weksteen9791b302021-03-03 16:30:20 +01008#![allow(dead_code)]
Joel Galenson4be0c6d2020-07-07 13:20:14 -07009use 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.
29pub struct LazyUsize(AtomicUsize);
30
31impl LazyUsize {
32 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.
42 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.
54pub struct LazyBool(LazyUsize);
55
56impl LazyBool {
57 pub const fn new() -> Self {
58 Self(LazyUsize::new())
59 }
60
61 pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
62 self.0.unsync_init(|| init() as usize) != 0
63 }
64}