use std::process::{Child, ExitStatus};
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::time::Duration;
use crate::constants::timeouts;
use crate::runutil::{ChildTerminationReason, ChildWaitOptions, wait_for_child_with_callbacks};
use super::types::TerminationReason;
pub(super) struct ManagedChildOutcome {
pub status: ExitStatus,
pub termination: Option<TerminationReason>,
}
pub(super) fn wait_for_child_owned(
child: Child,
timeout: Duration,
cancellation: Option<Arc<AtomicBool>>,
) -> std::io::Result<ManagedChildOutcome> {
let pid = child.id();
let outcome = wait_for_child_with_callbacks(
child,
ChildWaitOptions {
timeout: Some(timeout),
cancellation: cancellation.as_deref(),
poll_interval: timeouts::MANAGED_SUBPROCESS_POLL_INTERVAL,
interrupt_grace: timeouts::MANAGED_SUBPROCESS_INTERRUPT_GRACE,
},
move |child| signal_child(child, pid, false),
move |child| signal_child(child, pid, true),
)?;
Ok(ManagedChildOutcome {
status: outcome.status,
termination: if outcome.status.success() {
None
} else {
outcome.termination.map(map_termination_reason)
},
})
}
fn map_termination_reason(reason: ChildTerminationReason) -> TerminationReason {
match reason {
ChildTerminationReason::Timeout => TerminationReason::Timeout,
ChildTerminationReason::Cancelled => TerminationReason::Cancelled,
}
}
#[cfg(unix)]
fn signal_child(_child: &mut Child, pid: u32, hard_kill: bool) {
let signal = if hard_kill {
libc::SIGKILL
} else {
libc::SIGINT
};
unsafe {
let _ = libc::kill(-(pid as i32), signal);
}
}
#[cfg(not(unix))]
fn signal_child(child: &mut Child, _pid: u32, _hard_kill: bool) {
let _ = child.kill();
}