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