]> git.proxmox.com Git - rustc.git/blob - vendor/getrandom/src/util.rs
New upstream version 1.38.0+dfsg1
[rustc.git] / vendor / getrandom / src / util.rs
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 laziliy initialized static usize value. Useful
12 // when it is perferable 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 syncronization methods with be needed.
29 pub struct LazyUsize(AtomicUsize);
30
31 impl 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 // The initialization is currently running.
39 pub const ACTIVE: usize = usize::max_value() - 1;
40
41 // Runs the init() function at least once, returning the value of some run
42 // of init(). Multiple callers can run their init() functions in parallel.
43 // init() should always return the same value, if it succeeds.
44 pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
45 // Relaxed ordering is fine, as we only have a single atomic variable.
46 let mut val = self.0.load(Relaxed);
47 if val == Self::UNINIT {
48 val = init();
49 self.0.store(val, Relaxed);
50 }
51 val
52 }
53
54 // Synchronously runs the init() function. Only one caller will have their
55 // init() function running at a time, and exactly one successful call will
56 // be run. init() returning UNINIT or ACTIVE will be considered a failure,
57 // and future calls to sync_init will rerun their init() function.
58 pub fn sync_init(&self, init: impl FnOnce() -> usize, mut wait: impl FnMut()) -> usize {
59 // Common and fast path with no contention. Don't wast time on CAS.
60 match self.0.load(Relaxed) {
61 Self::UNINIT | Self::ACTIVE => {}
62 val => return val,
63 }
64 // Relaxed ordering is fine, as we only have a single atomic variable.
65 loop {
66 match self.0.compare_and_swap(Self::UNINIT, Self::ACTIVE, Relaxed) {
67 Self::UNINIT => {
68 let val = init();
69 self.0.store(
70 match val {
71 Self::UNINIT | Self::ACTIVE => Self::UNINIT,
72 val => val,
73 },
74 Relaxed,
75 );
76 return val;
77 }
78 Self::ACTIVE => wait(),
79 val => return val,
80 }
81 }
82 }
83 }
84
85 // Identical to LazyUsize except with bool instead of usize.
86 pub struct LazyBool(LazyUsize);
87
88 impl LazyBool {
89 pub const fn new() -> Self {
90 Self(LazyUsize::new())
91 }
92
93 pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
94 self.0.unsync_init(|| init() as usize) != 0
95 }
96 }