use crate::scenario::payload_run::PayloadHandle;
use crate::worker_ready::{WORKER_READY_MARKER_PREFIX, worker_ready_marker_path};
pub fn wait_for_worker_ready(
worker: &mut PayloadHandle,
worker_pid: u32,
timeout: std::time::Duration,
role: &str,
exit_code_legend: &str,
) -> anyhow::Result<()> {
use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
use std::os::unix::io::AsFd;
let ready_path = worker_ready_marker_path(worker_pid);
let deadline = std::time::Instant::now() + timeout;
let marker_dir = std::path::Path::new(WORKER_READY_MARKER_PREFIX)
.parent()
.unwrap_or_else(|| std::path::Path::new("/tmp"));
let inotify_result =
Inotify::init(InitFlags::IN_CLOEXEC | InitFlags::IN_NONBLOCK).and_then(|i| {
i.add_watch(
marker_dir,
AddWatchFlags::IN_CREATE | AddWatchFlags::IN_MOVED_TO,
)?;
Ok(i)
});
while !std::path::Path::new(&ready_path).exists() {
if let Some((_, metrics)) = worker.try_wait()? {
anyhow::bail!(
"{role} pid={worker_pid} exited before creating ready marker \
{ready_path} (exit_code={} — see stderr; worker exit codes: \
{exit_code_legend})",
metrics.exit_code,
);
}
let now = std::time::Instant::now();
if now >= deadline {
anyhow::bail!(
"{role} pid={worker_pid} did not create ready marker {ready_path} \
within {timeout:?}",
);
}
let remaining_ms = deadline
.duration_since(now)
.as_millis()
.min(u16::MAX as u128) as u16;
match inotify_result.as_ref() {
Ok(inotify) => {
let fd = inotify.as_fd();
let mut pollfds = [PollFd::new(fd, PollFlags::POLLIN)];
let _ = poll(&mut pollfds, PollTimeout::from(remaining_ms));
let _ = inotify.read_events();
}
Err(_) => {
std::thread::sleep(
std::time::Duration::from_millis(10).min(deadline.duration_since(now)),
);
}
}
}
if let Some((_, metrics)) = worker.try_wait()? {
anyhow::bail!(
"{role} pid={worker_pid} exited after writing ready marker but \
before the caller's next dispatch (exit_code={} — see stderr)",
metrics.exit_code,
);
}
Ok(())
}