use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
use tokio::sync::Mutex as AsyncMutex;
use super::slot_token::SlotToken;
use crate::dal::DAL;
use crate::database::universal_types::UniversalUuid;
#[derive(Clone)]
pub(crate) struct DeferralEntry {
pub(crate) slot: Arc<AsyncMutex<SlotToken>>,
pub(crate) dal: DAL,
}
fn registry() -> &'static Mutex<HashMap<UniversalUuid, DeferralEntry>> {
static REGISTRY: OnceLock<Mutex<HashMap<UniversalUuid, DeferralEntry>>> = OnceLock::new();
REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
}
pub(crate) fn register(
task_execution_id: UniversalUuid,
slot: Arc<AsyncMutex<SlotToken>>,
dal: DAL,
) {
registry()
.lock()
.expect("deferral registry poisoned")
.insert(task_execution_id, DeferralEntry { slot, dal });
}
pub(crate) fn deregister(task_execution_id: &UniversalUuid) {
registry()
.lock()
.expect("deferral registry poisoned")
.remove(task_execution_id);
}
pub(crate) fn lookup(task_execution_id: &UniversalUuid) -> Option<DeferralEntry> {
registry()
.lock()
.expect("deferral registry poisoned")
.get(task_execution_id)
.cloned()
}
#[cfg(test)]
pub(crate) fn len() -> usize {
registry().lock().expect("poisoned").len()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::executor::slot_token::SlotToken;
use std::sync::Arc;
use tokio::sync::Semaphore;
async fn a_slot() -> Arc<AsyncMutex<SlotToken>> {
let sem = Arc::new(Semaphore::new(1));
let permit = sem.clone().acquire_owned().await.unwrap();
Arc::new(AsyncMutex::new(SlotToken::new(permit, sem)))
}
#[cfg(feature = "sqlite")]
#[tokio::test]
async fn lookup_returns_the_same_slot_arc() {
let id = UniversalUuid::new_v4();
let slot = a_slot().await;
register(
id,
Arc::clone(&slot),
DAL::new(crate::database::Database::new(
&format!(
"file:defreg_{}?mode=memory&cache=shared",
uuid::Uuid::new_v4()
),
"",
2,
)),
);
let found = lookup(&id).expect("registered");
assert!(
Arc::ptr_eq(&found.slot, &slot),
"registry must share the slot, not clone the token"
);
deregister(&id);
assert!(lookup(&id).is_none(), "deregister must remove the entry");
}
#[tokio::test]
async fn lookup_after_deregister_is_none() {
let id = UniversalUuid::new_v4();
assert!(lookup(&id).is_none());
}
}