use anyhow::Result;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
pub fn run_engine_loop(
root: &Path,
cancel: Arc<AtomicBool>,
interval_secs: u64,
max_concurrent: usize,
skip_permissions: bool,
epic_filter: Option<String>,
) -> Result<()> {
let mut workers: Vec<(String, Option<String>, crate::start::ManagedChild, std::path::PathBuf)> = Vec::new();
let mut no_more = false;
let mut next_poll = Instant::now();
loop {
if cancel.load(Ordering::Relaxed) {
break;
}
let mut reaped = false;
workers.retain_mut(|(_, _epic_id, child, _pid_path)| {
let done = matches!(child.try_wait(), Ok(Some(_)));
if done {
reaped = true;
}
!done
});
if reaped {
next_poll = Instant::now();
no_more = false;
}
if no_more {
let now = Instant::now();
if now < next_poll {
std::thread::sleep(Duration::from_millis(500));
continue;
}
no_more = false;
}
if !no_more && workers.len() < max_concurrent {
let (blocked_epics, default_blocked) = crate::config::Config::load(root)
.map(|config| {
let epic_ids: Vec<Option<String>> = workers.iter()
.map(|(_, eid, _, _)| eid.clone())
.collect();
let blocked = config.blocked_epics(&epic_ids);
let def_blocked = config.is_default_branch_blocked(&epic_ids);
(blocked, def_blocked)
})
.unwrap_or_default();
let mut _messages = Vec::new();
let mut _warnings = Vec::new();
match crate::start::spawn_next_worker(root, true, skip_permissions, epic_filter.as_deref(), &blocked_epics, default_blocked, &mut _messages, &mut _warnings) {
Ok(None) => {
next_poll = Instant::now() + Duration::from_secs(interval_secs);
no_more = true;
}
Ok(Some((id, epic_id, child, pid_path))) => {
workers.push((id, epic_id, child, pid_path));
no_more = false;
}
Err(_) => {
no_more = true;
std::thread::sleep(Duration::from_secs(30));
}
}
} else {
std::thread::sleep(Duration::from_millis(500));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cancel_flag_stops_loop_immediately() {
let cancel = Arc::new(AtomicBool::new(true));
let root = std::path::Path::new("/nonexistent");
let result = run_engine_loop(root, cancel, 30, 1, false, None);
assert!(result.is_ok());
}
}