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, service_permissions) = 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, None)
216 }
217 OpenIdentity::ServicePrincipal { principal_id } => {
218 let permissions = Vec::new();
219 let principal = service_principal(principal_id, permissions.clone());
220 (
221 crate::handle::HandleIdentity::ServicePrincipal { principal_id },
222 Some(principal),
223 Some(permissions),
224 )
225 }
226 OpenIdentity::ScopedServicePrincipal {
227 principal_id,
228 permissions,
229 } => {
230 let principal = service_principal(principal_id, permissions.clone());
231 (
232 crate::handle::HandleIdentity::ServicePrincipal { principal_id },
233 Some(principal),
234 Some(permissions),
235 )
236 }
237 OpenIdentity::CatalogCredentials { username, password } => {
238 if !unbound.require_auth_enabled() {
239 return Err(crate::error::MongrelError::AuthNotRequired);
240 }
241 let principal = unbound
242 .authenticate_principal(&username, password.expose())?
243 .ok_or_else(|| crate::error::MongrelError::InvalidCredentials {
244 username: username.clone(),
245 })?;
246 let identity = crate::handle::HandleIdentity::CatalogUser {
247 username: principal.username.clone(),
248 user_id: principal.user_id,
249 created_version: principal.created_epoch,
250 };
251 (identity, Some(principal), None)
252 }
253 };
254 let facade = Database::from_core(core, principal, true);
255 Ok(DatabaseHandle::new(
256 facade,
257 handle_identity,
258 access,
259 service_permissions,
260 ))
261 }
262
263 pub fn registered_entries(&self) -> usize {
265 self.entries.lock().len()
266 }
267
268 pub(crate) fn mark_closing(&self, identity: &DatabaseFileIdentity, core: &Arc<DatabaseCore>) {
271 let mut entries = self.entries.lock();
272 if entries.get(identity).is_some_and(
273 |entry| matches!(entry, CoreEntry::Open(weak) if weak.as_ptr() == Arc::as_ptr(core)),
274 ) {
275 entries.insert(identity.clone(), CoreEntry::Closing);
276 }
277 }
278
279 pub(crate) fn entry_closed(&self, identity: &DatabaseFileIdentity, core: &Arc<DatabaseCore>) {
282 let mut entries = self.entries.lock();
283 let owned = match entries.get(identity) {
284 Some(CoreEntry::Closing) => true,
285 Some(CoreEntry::Open(weak)) => weak.as_ptr() == Arc::as_ptr(core),
286 _ => false,
287 };
288 if owned {
289 entries.remove(identity);
290 }
291 }
292}
293
294fn service_principal(
295 principal_id: [u8; 16],
296 permissions: Vec<crate::auth::Permission>,
297) -> crate::auth::Principal {
298 crate::auth::Principal {
299 user_id: 0,
300 created_epoch: 0,
301 username: format!("service:{principal_id:02x?}"),
302 is_admin: false,
303 roles: Vec::new(),
304 permissions,
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn wait_cell_wakes_all_waiters() {
314 let cell = OpenWaitCell::new();
315 let waiter = {
316 let cell = cell.clone();
317 std::thread::spawn(move || cell.wait())
318 };
319 std::thread::sleep(std::time::Duration::from_millis(10));
320 cell.fire();
321 waiter.join().unwrap();
322 cell.wait();
324 }
325
326 #[test]
327 fn global_is_a_singleton() {
328 assert!(std::ptr::eq(
329 DatabaseManager::global(),
330 DatabaseManager::global()
331 ));
332 }
333}