Coverage Report

Created: 2025-02-25 06:39

/rust/registry/src/index.crates.io-6f17d22bba15001f/protobuf-2.28.0/src/lazy.rs
Line
Count
Source (jump to first uncovered line)
1
//! Lazily initialized data.
2
//! Used in generated code.
3
4
// Avoid deprecation warnings when compiling rust-protobuf
5
#![allow(deprecated)]
6
7
use std::mem;
8
use std::sync;
9
10
/// Lasily initialized data.
11
#[deprecated(
12
    since = "2.16",
13
    note = "Please regenerate .rs files from .proto files to use newer APIs"
14
)]
15
pub struct Lazy<T> {
16
    #[doc(hidden)]
17
    pub lock: sync::Once,
18
    #[doc(hidden)]
19
    pub ptr: *const T,
20
}
21
22
impl<T> Lazy<T> {
23
    /// Uninitialized `Lazy` object.
24
    ///
25
    /// The initializer is added in rust-protobuf 2.11, for compatibility with
26
    /// previously generated code, existing fields are kept public.
27
    pub const INIT: Lazy<T> = Lazy {
28
        lock: sync::Once::new(),
29
        ptr: 0 as *const T,
30
    };
31
32
    /// Get lazy field value, initialize it with given function if not yet.
33
0
    pub fn get<F>(&'static mut self, init: F) -> &'static T
34
0
    where
35
0
        F: FnOnce() -> T,
36
0
    {
37
0
        // ~ decouple the lifetimes of 'self' and 'self.lock' such we
38
0
        // can initialize self.ptr in the call_once closure (note: we
39
0
        // do have to initialize self.ptr in the closure to guarantee
40
0
        // the ptr is valid for all calling threads at any point in
41
0
        // time)
42
0
        let lock: &sync::Once = unsafe { mem::transmute(&self.lock) };
43
0
        lock.call_once(|| unsafe {
44
0
            self.ptr = mem::transmute(Box::new(init()));
45
0
        });
46
0
        unsafe { &*self.ptr }
47
0
    }
48
}
49
50
/// Used to initialize `lock` field in `Lazy` struct.
51
#[deprecated(
52
    since = "2.11",
53
    note = "Regenerate .proto files to use safer initializer"
54
)]
55
pub const ONCE_INIT: sync::Once = sync::Once::new();
56
57
#[cfg(test)]
58
mod test {
59
    use std::sync::atomic::AtomicIsize;
60
    use std::sync::atomic::Ordering;
61
    use std::sync::Arc;
62
    use std::sync::Barrier;
63
    use std::thread;
64
65
    use super::Lazy;
66
67
    #[test]
68
    fn many_threads_calling_get() {
69
        const N_THREADS: usize = 32;
70
        const N_ITERS_IN_THREAD: usize = 32;
71
        const N_ITERS: usize = 16;
72
73
        static mut LAZY: Lazy<String> = Lazy::INIT;
74
        static CALL_COUNT: AtomicIsize = AtomicIsize::new(0);
75
76
        let value = "Hello, world!".to_owned();
77
78
        for _ in 0..N_ITERS {
79
            // Reset mutable state.
80
            unsafe {
81
                LAZY = Lazy::INIT;
82
            }
83
            CALL_COUNT.store(0, Ordering::SeqCst);
84
85
            // Create a bunch of threads, all calling .get() at the same time.
86
            let mut threads = vec![];
87
            let barrier = Arc::new(Barrier::new(N_THREADS));
88
89
            for _ in 0..N_THREADS {
90
                let cloned_value_thread = value.clone();
91
                let cloned_barrier = barrier.clone();
92
                threads.push(thread::spawn(move || {
93
                    // Ensure all threads start at once to maximise contention.
94
                    cloned_barrier.wait();
95
                    for _ in 0..N_ITERS_IN_THREAD {
96
                        assert_eq!(&cloned_value_thread, unsafe {
97
                            LAZY.get(|| {
98
                                CALL_COUNT.fetch_add(1, Ordering::SeqCst);
99
                                cloned_value_thread.clone()
100
                            })
101
                        });
102
                    }
103
                }));
104
            }
105
106
            for thread in threads {
107
                thread.join().unwrap();
108
            }
109
110
            assert_eq!(CALL_COUNT.load(Ordering::SeqCst), 1);
111
        }
112
    }
113
}