use std::collections::{HashMap, HashSet, VecDeque};
use crate::engine::dispatch_entry::FunctionKey;
use crate::ids::{ComponentRef, ExecId};
#[derive(Clone, Debug)]
pub struct ModuleBootstrap {
pub function_key: FunctionKey,
pub touch_set: HashSet<ComponentRef>,
}
#[derive(Clone, Debug)]
pub struct ComponentBootstrap {
pub cref: ComponentRef,
}
#[derive(Clone, Debug)]
pub enum BootstrapKind {
Module {
target: String,
},
Component {
slot: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BootstrapStatus {
Idle,
Running,
WaitingForInput,
}
pub struct BootstrapRequest<'a> {
pub target: &'a str,
pub inputs: &'a [(&'a str, &'a [u8])],
}
#[derive(Clone, Debug)]
pub struct OwnedBootstrapRequest {
pub target_name: String,
pub inputs: Vec<(String, Vec<u8>)>,
}
#[derive(Clone, Debug)]
pub struct InFlightBootstrap {
pub kind: BootstrapKind,
pub exec_id: ExecId,
pub touch_set: HashSet<ComponentRef>,
pub staged_inputs: HashSet<String>,
}
#[derive(Clone, Debug)]
pub struct ReadyBootstrap {
pub kind: BootstrapKind,
pub touch_set: HashSet<ComponentRef>,
pub staged_inputs: HashSet<String>,
}
#[derive(Clone, Debug)]
pub struct QueuedBootstrap {
pub kind: BootstrapKind,
pub touch_set: HashSet<ComponentRef>,
pub staged_inputs: HashSet<String>,
}
pub(crate) struct BootstrapState {
pub(crate) module_bootstraps: HashMap<String, ModuleBootstrap>,
pub(crate) component_bootstraps: HashMap<String, ComponentBootstrap>,
pub(crate) install_order: Vec<String>,
pub(crate) pending_requests: VecDeque<OwnedBootstrapRequest>,
pub(crate) in_flight: Vec<InFlightBootstrap>,
pub(crate) waiting: VecDeque<QueuedBootstrap>,
pub(crate) next_idx: usize,
pub(crate) pending: bool,
}
impl BootstrapState {
pub(crate) fn new() -> Self {
Self {
module_bootstraps: HashMap::new(),
component_bootstraps: HashMap::new(),
install_order: Vec::new(),
pending_requests: VecDeque::new(),
in_flight: Vec::new(),
waiting: VecDeque::new(),
next_idx: 0,
pending: false,
}
}
pub(crate) fn clear_for_restore(&mut self) {
self.pending = false;
self.in_flight.clear();
self.pending_requests.clear();
self.waiting.clear();
self.next_idx = self.install_order.len();
}
pub(crate) fn first_function_key(&self) -> Option<&FunctionKey> {
let name = self.install_order.first()?;
self.module_bootstraps.get(name).map(|m| &m.function_key)
}
pub(crate) fn function_keys(&self) -> Vec<FunctionKey> {
self.install_order
.iter()
.filter_map(|name| {
self.module_bootstraps
.get(name)
.map(|m| m.function_key.clone())
})
.collect()
}
pub(crate) fn module_exec_id(&self) -> Option<ExecId> {
self.in_flight.iter().find_map(|b| match b.kind {
BootstrapKind::Module { .. } => Some(b.exec_id),
BootstrapKind::Component { .. } => None,
})
}
pub(crate) fn register_module(&mut self, function_key: FunctionKey) {
let name = function_key.1.clone();
let entry = self
.module_bootstraps
.entry(name.clone())
.or_insert_with(|| ModuleBootstrap {
function_key: function_key.clone(),
touch_set: HashSet::new(),
});
entry.function_key = function_key;
if !self.install_order.iter().any(|n| n == &name) {
self.install_order.push(name);
}
}
pub(crate) fn arm_install_order(&mut self) -> bool {
if self.next_idx >= self.install_order.len() {
return false;
}
self.pending = true;
true
}
pub(crate) fn mark_module_in_flight(
&mut self,
target: String,
exec_id: ExecId,
touch_set: HashSet<ComponentRef>,
) {
self.in_flight.push(InFlightBootstrap {
kind: BootstrapKind::Module { target },
exec_id,
touch_set,
staged_inputs: HashSet::new(),
});
}
pub(crate) fn component_bootstrap(&self, slot: &str) -> Option<&ComponentBootstrap> {
self.component_bootstraps.get(slot)
}
#[cfg(test)]
pub(crate) fn enqueue_request(&mut self, req: OwnedBootstrapRequest) {
self.pending_requests.push_back(req);
self.pending = true;
}
pub(crate) fn process_pending_requests(&mut self) -> Vec<ReadyBootstrap> {
let mut ready = Vec::new();
while let Some(req) = self.pending_requests.pop_front() {
let Some(meta) = self.module_bootstraps.get(&req.target_name) else {
continue;
};
let touch_set = meta.touch_set.clone();
let kind = BootstrapKind::Module {
target: req.target_name.clone(),
};
let staged_inputs: HashSet<String> =
req.inputs.iter().map(|(name, _)| name.clone()).collect();
if Self::overlaps_any_in_flight(&self.in_flight, &touch_set) {
self.waiting.push_back(QueuedBootstrap {
kind,
touch_set,
staged_inputs,
});
} else {
ready.push(ReadyBootstrap {
kind,
touch_set,
staged_inputs,
});
}
}
ready
}
pub(crate) fn on_bootstrap_drained(&mut self, exec_id: ExecId) -> Vec<ReadyBootstrap> {
self.in_flight.retain(|b| b.exec_id != exec_id);
let mut promoted = Vec::new();
let mut remaining = VecDeque::new();
while let Some(waiter) = self.waiting.pop_front() {
if Self::overlaps_any_in_flight(&self.in_flight, &waiter.touch_set) {
remaining.push_back(waiter);
} else {
promoted.push(ReadyBootstrap {
kind: waiter.kind,
touch_set: waiter.touch_set,
staged_inputs: waiter.staged_inputs,
});
}
}
self.waiting = remaining;
promoted
}
fn overlaps_any_in_flight(
in_flight: &[InFlightBootstrap],
touch_set: &HashSet<ComponentRef>,
) -> bool {
if touch_set.is_empty() {
return false;
}
in_flight
.iter()
.any(|b| !b.touch_set.is_disjoint(touch_set))
}
}