opendev_runtime/
lazy_init.rs1use std::fmt;
7use std::future::Future;
8use std::sync::Arc;
9
10use tokio::sync::OnceCell;
11use tracing::debug;
12
13pub struct LazySubsystem<T: Send + Sync + 'static> {
22 name: &'static str,
23 cell: Arc<OnceCell<T>>,
24}
25
26impl<T: Send + Sync + 'static> LazySubsystem<T> {
27 pub fn new(name: &'static str) -> Self {
29 Self {
30 name,
31 cell: Arc::new(OnceCell::new()),
32 }
33 }
34
35 pub async fn get<F, Fut>(&self, init: F) -> &T
39 where
40 F: FnOnce() -> Fut,
41 Fut: Future<Output = T>,
42 {
43 self.cell
44 .get_or_init(|| async {
45 debug!("Lazy-initializing subsystem: {}", self.name);
46 let value = init().await;
47 debug!("Subsystem {} initialized", self.name);
48 value
49 })
50 .await
51 }
52
53 pub async fn get_or_try_init<F, Fut, E>(&self, init: F) -> Result<&T, E>
58 where
59 F: FnOnce() -> Fut,
60 Fut: Future<Output = Result<T, E>>,
61 {
62 let name = self.name;
63 self.cell
64 .get_or_try_init(|| async {
65 debug!("Lazy-initializing subsystem (fallible): {name}");
66 let value = init().await?;
67 debug!("Subsystem {name} initialized");
68 Ok(value)
69 })
70 .await
71 }
72
73 pub fn is_initialized(&self) -> bool {
75 self.cell.initialized()
76 }
77
78 pub fn try_get(&self) -> Option<&T> {
80 self.cell.get()
81 }
82
83 pub fn name(&self) -> &'static str {
85 self.name
86 }
87}
88
89impl<T: Send + Sync + 'static> Clone for LazySubsystem<T> {
90 fn clone(&self) -> Self {
91 Self {
92 name: self.name,
93 cell: Arc::clone(&self.cell),
94 }
95 }
96}
97
98impl<T: Send + Sync + fmt::Debug + 'static> fmt::Debug for LazySubsystem<T> {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_struct("LazySubsystem")
101 .field("name", &self.name)
102 .field("initialized", &self.is_initialized())
103 .finish()
104 }
105}
106
107pub type LazyLsp<T> = LazySubsystem<T>;
113
114pub type LazyMcp<T> = LazySubsystem<T>;
116
117pub type LazyEmbeddings<T> = LazySubsystem<T>;
119
120pub fn create_lazy_subsystems<L, M, E>() -> (LazyLsp<L>, LazyMcp<M>, LazyEmbeddings<E>)
122where
123 L: Send + Sync + 'static,
124 M: Send + Sync + 'static,
125 E: Send + Sync + 'static,
126{
127 (
128 LazySubsystem::new("LSP"),
129 LazySubsystem::new("MCP"),
130 LazySubsystem::new("Embeddings"),
131 )
132}
133
134pub struct SyncLazy<T: Send + Sync + 'static> {
142 name: &'static str,
143 cell: std::sync::OnceLock<T>,
144}
145
146impl<T: Send + Sync + 'static> SyncLazy<T> {
147 pub const fn new(name: &'static str) -> Self {
149 Self {
150 name,
151 cell: std::sync::OnceLock::new(),
152 }
153 }
154
155 pub fn get_or_init(&self, init: impl FnOnce() -> T) -> &T {
157 self.cell.get_or_init(|| {
158 debug!("Sync lazy-init: {}", self.name);
159 init()
160 })
161 }
162
163 pub fn is_initialized(&self) -> bool {
165 self.cell.get().is_some()
166 }
167
168 pub fn try_get(&self) -> Option<&T> {
170 self.cell.get()
171 }
172
173 pub fn name(&self) -> &'static str {
175 self.name
176 }
177}
178
179impl<T: Send + Sync + fmt::Debug + 'static> fmt::Debug for SyncLazy<T> {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 f.debug_struct("SyncLazy")
182 .field("name", &self.name)
183 .field("initialized", &self.is_initialized())
184 .finish()
185 }
186}
187
188#[cfg(test)]
189#[path = "lazy_init_tests.rs"]
190mod tests;