xref: /DragonOS/kernel/src/libs/lazy_init.rs (revision 766127209ee49465a8086cfd0bec90d8b79a96c0)
1 // Copyright (C) DragonOS Community  longjin
2 
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License
5 // as published by the Free Software Foundation; either version 2
6 // of the License, or (at your option) any later version.
7 
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16 // Or you can visit https://www.gnu.org/licenses/gpl-2.0.html
17 #![allow(dead_code)]
18 
19 use core::cell::UnsafeCell;
20 use core::fmt::Debug;
21 use core::mem::MaybeUninit;
22 use core::ops::{Deref, DerefMut};
23 use core::sync::atomic::{AtomicBool, Ordering};
24 
25 use super::spinlock::SpinLock;
26 
27 /// A wrapper around a value that is initialized lazily.
28 /// The value is initialized the first time it is accessed.
29 /// This is useful for initializing values that are expensive to initialize.
30 /// The value is initialized using the provided closure.
31 /// The closure is only called once, and the result is cached.
32 /// The value is not initialized if it is never accessed.
33 pub struct Lazy<T: Sync> {
34     /// The lock that is used to ensure that only one thread calls the init function at the same time.
35     init_lock: SpinLock<()>,
36     /// The value that is initialized lazily.
37     value: UnsafeCell<MaybeUninit<T>>,
38     /// Whether the value has been initialized.
39     initialized: AtomicBool,
40 }
41 
42 impl<T: Sync> Lazy<T> {
43     /// Creates a new `Lazy` value that will be initialized with the
44     /// result of the closure `init`.
45     pub const fn new() -> Lazy<T> {
46         Lazy {
47             value: UnsafeCell::new(MaybeUninit::uninit()),
48             initialized: AtomicBool::new(false),
49             init_lock: SpinLock::new(()),
50         }
51     }
52 
53     /// Returns true if the value has been initialized.
54     #[inline(always)]
55     pub fn initialized(&self) -> bool {
56         let initialized = self.initialized.load(Ordering::Acquire);
57         if initialized {
58             return true;
59         }
60         return false;
61     }
62 
63     /// Ensures that this lazy value is initialized. If the value has not
64     /// yet been initialized, this will raise a panic.
65     #[inline(always)]
66     pub fn ensure(&self) {
67         if self.initialized() {
68             return;
69         }
70         panic!("Lazy value was not initialized");
71     }
72 
73     pub fn init(&self, value: T) {
74         assert!(!self.initialized());
75 
76         // We need this lock to ensure that only one thread calls the init function at the same time.
77         let _init_guard = self.init_lock.lock();
78         // Check again, in case another thread initialized it while we were waiting for the lock.
79         let initialized = self.initialized();
80         if initialized {
81             return;
82         }
83         unsafe {
84             (*self.value.get()).as_mut_ptr().write(value);
85         }
86         self.initialized.store(true, Ordering::Release);
87     }
88     /// Forces the evaluation of this lazy value and returns a reference to
89     /// the result. This is equivalent to the `Deref` impl, but is explicit.
90     /// This will initialize the value if it has not yet been initialized.
91     pub fn get(&self) -> &T {
92         self.ensure();
93         return unsafe { self.get_unchecked() };
94     }
95 
96     /// Returns a reference to the value if it has been initialized.
97     /// Otherwise, returns `None`.
98     pub fn try_get(&self) -> Option<&T> {
99         if self.initialized() {
100             return Some(unsafe { self.get_unchecked() });
101         }
102         return None;
103     }
104 
105     /// Forces the evaluation of this lazy value and returns a mutable
106     /// reference to the result. This is equivalent to the `DerefMut` impl,
107     /// but is explicit. This will initialize the value if it has not yet
108     /// been initialized.
109     pub fn get_mut(&mut self) -> &mut T {
110         self.ensure();
111         return unsafe { self.get_mut_unchecked() };
112     }
113 
114     #[inline(always)]
115     pub unsafe fn get_unchecked(&self) -> &T {
116         return &*(*self.value.get()).as_ptr();
117     }
118 
119     #[inline(always)]
120     pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
121         return &mut *(*self.value.get()).as_mut_ptr();
122     }
123 }
124 
125 impl<T: Sync> Deref for Lazy<T> {
126     type Target = T;
127 
128     #[inline(always)]
129     fn deref(&self) -> &T {
130         return self.get();
131     }
132 }
133 
134 impl<T: Sync> DerefMut for Lazy<T> {
135     #[inline(always)]
136     fn deref_mut(&mut self) -> &mut T {
137         return self.get_mut();
138     }
139 }
140 
141 impl<T: Sync + Debug> Debug for Lazy<T> {
142     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
143         if let Some(value) = self.try_get() {
144             return write!(f, "Lazy({:?})", value);
145         } else {
146             return write!(f, "Lazy(uninitialized)");
147         }
148     }
149 }
150 
151 impl<T: Sync> Drop for Lazy<T> {
152     fn drop(&mut self) {
153         if self.initialized() {
154             unsafe {
155                 (*self.value.get()).as_mut_ptr().drop_in_place();
156             }
157         }
158     }
159 }
160