Merge nucleic/sleek-thistle-egret-fyej into dev
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
//! Process management
|
||||
|
||||
use futures::FutureExt;
|
||||
|
||||
use crate::{error, sys};
|
||||
|
||||
/// A waitable future that will yield the results of a child process's execution.
|
||||
pub(crate) type WaitableChildProcess = std::pin::Pin<
|
||||
Box<dyn futures::Future<Output = Result<std::process::Output, std::io::Error>> + Send + Sync>,
|
||||
>;
|
||||
|
||||
/// Tracks a child process being awaited.
|
||||
pub struct ChildProcess {
|
||||
/// A waitable future that will yield the results of a child process's execution.
|
||||
exec_future: WaitableChildProcess,
|
||||
/// If available, the process ID of the child.
|
||||
pid: Option<sys::process::ProcessId>,
|
||||
/// If available, the process group ID of the child.
|
||||
pgid: Option<sys::process::ProcessId>,
|
||||
}
|
||||
|
||||
impl ChildProcess {
|
||||
/// Wraps a child process and its future.
|
||||
pub fn new(
|
||||
child: sys::process::Child,
|
||||
pid: Option<sys::process::ProcessId>,
|
||||
pgid: Option<sys::process::ProcessId>,
|
||||
) -> Self {
|
||||
Self {
|
||||
exec_future: Box::pin(child.wait_with_output()),
|
||||
pid,
|
||||
pgid,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the process's ID.
|
||||
pub const fn pid(&self) -> Option<sys::process::ProcessId> {
|
||||
self.pid
|
||||
}
|
||||
|
||||
/// Returns the process's group ID.
|
||||
pub const fn pgid(&self) -> Option<sys::process::ProcessId> {
|
||||
self.pgid
|
||||
}
|
||||
|
||||
/// Waits for the process to exit.
|
||||
pub async fn wait(&mut self) -> Result<ProcessWaitResult, error::Error> {
|
||||
#[allow(unused_mut, reason = "only mutated on some platforms")]
|
||||
let mut sigtstp = sys::signal::tstp_signal_listener()?;
|
||||
#[allow(unused_mut, reason = "only mutated on some platforms")]
|
||||
let mut sigchld = sys::signal::chld_signal_listener()?;
|
||||
|
||||
#[allow(clippy::ignored_unit_patterns)]
|
||||
loop {
|
||||
tokio::select! {
|
||||
output = &mut self.exec_future => {
|
||||
break Ok(ProcessWaitResult::Completed(output?))
|
||||
},
|
||||
_ = sigtstp.recv() => {
|
||||
break Ok(ProcessWaitResult::Stopped)
|
||||
},
|
||||
_ = sigchld.recv() => {
|
||||
if sys::signal::poll_for_stopped_children()? {
|
||||
break Ok(ProcessWaitResult::Stopped);
|
||||
}
|
||||
},
|
||||
_ = sys::signal::await_ctrl_c() => {
|
||||
// SIGINT got thrown. Handle it and continue looping. The child should
|
||||
// have received it as well, and either handled it or ended up getting
|
||||
// terminated (in which case we'll see the child exit).
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn poll(&mut self) -> Option<Result<std::process::Output, error::Error>> {
|
||||
let checkable_future = &mut self.exec_future;
|
||||
checkable_future
|
||||
.now_or_never()
|
||||
.map(|result| result.map_err(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the result of waiting for an executing process.
|
||||
pub enum ProcessWaitResult {
|
||||
/// The process completed.
|
||||
Completed(std::process::Output),
|
||||
/// The process stopped and has not yet completed.
|
||||
Stopped,
|
||||
}
|
||||
Reference in New Issue
Block a user