use std::sync::{Arc, OnceLock};
use aion_core::{RunId, WorkflowId};
use dashmap::DashMap;
use tokio::sync::watch;
struct Gate {
reached: watch::Sender<bool>,
release: watch::Sender<bool>,
}
type GateKey = (WorkflowId, RunId);
fn gates() -> &'static DashMap<GateKey, Arc<Gate>> {
static GATES: OnceLock<DashMap<GateKey, Arc<Gate>>> = OnceLock::new();
GATES.get_or_init(DashMap::new)
}
pub(crate) async fn hold(id: &WorkflowId, run: &RunId) {
let Some(gate) = gates()
.get(&(id.clone(), run.clone()))
.map(|entry| Arc::clone(&entry))
else {
return;
};
gate.reached.send_replace(true);
let mut release = gate.release.subscribe();
drop(release.wait_for(|released| *released).await);
}
#[cfg(test)]
pub(crate) struct ArmedGate {
key: GateKey,
gate: Arc<Gate>,
}
#[cfg(test)]
impl ArmedGate {
pub(crate) async fn wait_until_reached(&self) {
let mut reached = self.gate.reached.subscribe();
drop(reached.wait_for(|reached| *reached).await);
}
pub(crate) fn was_reached(&self) -> bool {
*self.gate.reached.borrow()
}
}
#[cfg(test)]
impl Drop for ArmedGate {
fn drop(&mut self) {
self.gate.release.send_replace(true);
drop(gates().remove(&self.key));
}
}
#[cfg(test)]
pub(crate) fn arm(id: &WorkflowId, run: &RunId) -> ArmedGate {
let key = (id.clone(), run.clone());
let gate = Arc::new(Gate {
reached: watch::channel(false).0,
release: watch::channel(false).0,
});
gates().insert(key.clone(), Arc::clone(&gate));
ArmedGate { key, gate }
}