use std::{
fs, io,
path::Path,
process::Child,
thread,
time::{Duration, Instant},
};
pub fn kill_child_at_acknowledged_barrier(
child: &mut Child,
root: &Path,
timeout: Duration,
) -> io::Result<()> {
let ready_path = root.join("barrier-ready");
let acknowledge_path = root.join("barrier-acknowledged");
let armed_path = root.join("barrier-armed");
let handshake = (|| {
wait_for_child_path(child, &ready_path, "child barrier", timeout)?;
fs::write(&acknowledge_path, b"acknowledged\n")?;
wait_for_child_path(child, &armed_path, "armed child barrier", timeout)
})();
if let Err(error) = handshake {
let _ = child.kill();
let _ = child.wait();
return Err(error);
}
child.kill()?;
child.wait()?;
Ok(())
}
pub fn hold_at_acknowledged_barrier(root: &Path, timeout: Duration) -> ! {
let ready_path = root.join("barrier-ready");
let acknowledge_path = root.join("barrier-acknowledged");
let armed_path = root.join("barrier-armed");
fs::write(&ready_path, b"ready\n").expect("signal child barrier");
wait_for_path(&acknowledge_path, "parent barrier acknowledgement", timeout)
.expect("wait for parent barrier acknowledgement");
fs::write(&armed_path, b"armed\n").expect("arm child crash");
loop {
thread::sleep(Duration::from_secs(1));
}
}
pub fn wait_for_child_path(
child: &mut Child,
path: &Path,
description: &str,
timeout: Duration,
) -> io::Result<()> {
let deadline = Instant::now() + timeout;
while !path.is_file() {
if let Some(status) = child.try_wait()? {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!("crash child exited before {description}: {status}"),
));
}
if Instant::now() >= deadline {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
format!("timed out waiting for {description}"),
));
}
thread::sleep(Duration::from_millis(10));
}
Ok(())
}
pub fn wait_for_path(path: &Path, description: &str, timeout: Duration) -> io::Result<()> {
let deadline = Instant::now() + timeout;
while !path.is_file() {
if Instant::now() >= deadline {
return Err(io::Error::new(
io::ErrorKind::TimedOut,
format!("timed out waiting for {description}"),
));
}
thread::sleep(Duration::from_millis(10));
}
Ok(())
}
#[cfg(test)]
mod tests;