1use std::collections::HashMap;
22use std::sync::{Arc, OnceLock, Weak};
23
24use parking_lot::{Condvar, Mutex};
25
26use crate::core::DatabaseFileIdentity;
27use crate::database::{Database, DatabaseCore};
28use crate::error::Result;
29use crate::handle::{DatabaseHandle, HandleAccess, OpenIdentity};
30
31pub struct DatabaseManager {
33 entries: Mutex<HashMap<DatabaseFileIdentity, CoreEntry>>,
34}
35
36enum CoreEntry {
38 Opening(OpenWaitCell),
41 Open(Weak<DatabaseCore>),
45 Closing,
48}
49
50#[derive(Clone)]
53struct OpenWaitCell {
54 inner: Arc<(Mutex<bool>, Condvar)>,
55}
56
57impl OpenWaitCell {
58 fn new() -> Self {
59 Self {
60 inner: Arc::new((Mutex::new(false), Condvar::new())),
61 }
62 }
63
64 fn wait(&self) {
65 let (lock, condvar) = &*self.inner;
66 let mut ready = lock.lock();
67 while !*ready {
68 condvar.wait(&mut ready);
69 }
70 }
71
72 fn fire(&self) {
73 let (lock, condvar) = &*self.inner;
74 *lock.lock() = true;
75 condvar.notify_all();
76 }
77}
78
79struct OpeningGuard<'a> {
83 manager: &'a DatabaseManager,
84 identity: DatabaseFileIdentity,
85 cell: OpenWaitCell,
86 armed: bool,
87}
88
89impl OpeningGuard<'_> {
90 fn disarm(&mut self) {
91 self.armed = false;
92 }
93}
94
95impl Drop for OpeningGuard<'_> {
96 fn drop(&mut self) {
97 if !self.armed {
98 return;
99 }
100 let mut entries = self.manager.entries.lock();
101 if entries.get(&self.identity).is_some_and(
102 |entry| matches!(entry, CoreEntry::Opening(cell) if cell.same_as(&self.cell)),
103 ) {
104 entries.remove(&self.identity);
105 }
106 drop(entries);
107 self.cell.fire();
108 }
109}
110
111impl OpenWaitCell {
112 fn same_as(&self, other: &OpenWaitCell) -> bool {
113 Arc::ptr_eq(&self.inner, &other.inner)
114 }
115}
116
117pub(crate) struct CoreRegistration {
120 pub identity: DatabaseFileIdentity,
121 pub manager: &'static DatabaseManager,
122}
123
124impl DatabaseManager {
125 pub fn global() -> &'static DatabaseManager {
127 static MANAGER: OnceLock<DatabaseManager> = OnceLock::new();
128 MANAGER.get_or_init(|| DatabaseManager {
129 entries: Mutex::new(HashMap::new()),
130 })
131 }
132
133 pub fn open_shared(
142 self: &'static DatabaseManager,
143 root: impl AsRef<std::path::Path>,
144 identity: OpenIdentity,
145 ) -> Result<DatabaseHandle> {
146 self.open_shared_with_access(root, identity, HandleAccess::read_write())
147 }
148
149 pub fn open_shared_with_access(
151 self: &'static DatabaseManager,
152 root: impl AsRef<std::path::Path>,
153 identity: OpenIdentity,
154 access: HandleAccess,
155 ) -> Result<DatabaseHandle> {
156 let identity_key = DatabaseFileIdentity::for_path(root.as_ref())?;
157 let core = loop {
158 let mut entries = self.entries.lock();
159 match entries.get(&identity_key) {
160 Some(CoreEntry::Open(weak)) => {
161 if let Some(core) = weak.upgrade() {
162 if core.is_open() {
163 break core;
164 }
165 entries.remove(&identity_key);
169 } else {
170 entries.remove(&identity_key);
173 }
174 }
175 Some(CoreEntry::Opening(cell)) => {
176 let cell = cell.clone();
177 drop(entries);
178 cell.wait();
179 }
180 Some(CoreEntry::Closing) => {
181 entries.remove(&identity_key);
182 }
183 None => {
184 let cell = OpenWaitCell::new();
185 entries.insert(identity_key.clone(), CoreEntry::Opening(cell.clone()));
186 drop(entries);
187 let mut guard = OpeningGuard {
188 manager: self,
189 identity: identity_key.clone(),
190 cell: cell.clone(),
191 armed: true,
192 };
193 let core = Database::open_for_shared_core(root.as_ref())?.into_core();
196 core.set_registry(CoreRegistration {
197 identity: identity_key.clone(),
198 manager: self,
199 })?;
200 self.entries
201 .lock()
202 .insert(identity_key.clone(), CoreEntry::Open(Arc::downgrade(&core)));
203 guard.disarm();
204 cell.fire();
205 break core;
206 }
207 }
208 };
209 let unbound = Database::from_core(Arc::clone(&core), None, true);
210 let (handle_identity, principal) = match identity {
211 OpenIdentity::Credentialless => {
212 if unbound.require_auth_enabled() {
213 return Err(crate::error::MongrelError::AuthRequired);
214 }
215 (crate::handle::HandleIdentity::Credentialless, None)
216 }
217 OpenIdentity::ServiceCredentials { token_id, secret } => {
218 let def = core
221 .service_principals
222 .authenticate(&token_id, secret.expose())?;
223 let principal = service_principal_from_def(&def);
224 (
225 crate::handle::HandleIdentity::ServicePrincipal {
226 token_id: def.token_id,
227 principal_id: def.principal_id,
228 creation_version: def.creation_version,
229 },
230 Some(principal),
231 )
232 }
233 OpenIdentity::CatalogCredentials { username, password } => {
234 if !unbound.require_auth_enabled() {
235 return Err(crate::error::MongrelError::AuthNotRequired);
236 }
237 let principal = unbound
238 .authenticate_principal(&username, password.expose())?
239 .ok_or_else(|| crate::error::MongrelError::InvalidCredentials {
240 username: username.clone(),
241 })?;
242 let identity = crate::handle::HandleIdentity::CatalogUser {
243 username: principal.username.clone(),
244 user_id: principal.user_id,
245 created_version: principal.created_epoch,
246 };
247 (identity, Some(principal))
248 }
249 };
250 let facade = Database::from_core(core, principal, true);
251 Ok(DatabaseHandle::new(facade, handle_identity, access, None))
252 }
253
254 pub fn registered_entries(&self) -> usize {
256 self.entries.lock().len()
257 }
258
259 pub(crate) fn mark_closing(&self, identity: &DatabaseFileIdentity, core: &Arc<DatabaseCore>) {
262 let mut entries = self.entries.lock();
263 if entries.get(identity).is_some_and(
264 |entry| matches!(entry, CoreEntry::Open(weak) if weak.as_ptr() == Arc::as_ptr(core)),
265 ) {
266 entries.insert(identity.clone(), CoreEntry::Closing);
267 }
268 }
269
270 pub(crate) fn entry_closed(&self, identity: &DatabaseFileIdentity, core: &Arc<DatabaseCore>) {
273 let mut entries = self.entries.lock();
274 let owned = match entries.get(identity) {
275 Some(CoreEntry::Closing) => true,
276 Some(CoreEntry::Open(weak)) => weak.as_ptr() == Arc::as_ptr(core),
277 _ => false,
278 };
279 if owned {
280 entries.remove(identity);
281 }
282 }
283}
284
285fn service_principal_from_def(
286 def: &crate::service_principal::ServicePrincipalDefinition,
287) -> crate::auth::Principal {
288 crate::auth::Principal {
289 user_id: 0,
290 created_epoch: def.creation_version,
291 username: format!("service:{}", def.token_id),
292 is_admin: false,
293 roles: Vec::new(),
294 permissions: def.permissions.clone(),
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 #[test]
303 fn wait_cell_wakes_all_waiters() {
304 let cell = OpenWaitCell::new();
305 let waiter = {
306 let cell = cell.clone();
307 std::thread::spawn(move || cell.wait())
308 };
309 std::thread::sleep(std::time::Duration::from_millis(10));
310 cell.fire();
311 waiter.join().unwrap();
312 cell.wait();
314 }
315
316 #[test]
317 fn global_is_a_singleton() {
318 assert!(std::ptr::eq(
319 DatabaseManager::global(),
320 DatabaseManager::global()
321 ));
322 }
323}