1use futures::FutureExt;
4
5use crate::{error, sys};
6
7pub(crate) type WaitableChildProcess = std::pin::Pin<
9 Box<dyn futures::Future<Output = Result<std::process::Output, std::io::Error>> + Send + Sync>,
10>;
11
12pub struct ChildProcess {
14 pid: Option<sys::process::ProcessId>,
16 exec_future: WaitableChildProcess,
18}
19
20impl ChildProcess {
21 pub fn new(pid: Option<sys::process::ProcessId>, child: sys::process::Child) -> Self {
23 Self {
24 pid,
25 exec_future: Box::pin(child.wait_with_output()),
26 }
27 }
28
29 pub const fn pid(&self) -> Option<sys::process::ProcessId> {
31 self.pid
32 }
33
34 pub async fn wait(&mut self) -> Result<ProcessWaitResult, error::Error> {
36 #[allow(unused_mut, reason = "only mutated on some platforms")]
37 let mut sigtstp = sys::signal::tstp_signal_listener()?;
38 #[allow(unused_mut, reason = "only mutated on some platforms")]
39 let mut sigchld = sys::signal::chld_signal_listener()?;
40
41 #[allow(clippy::ignored_unit_patterns)]
42 loop {
43 tokio::select! {
44 output = &mut self.exec_future => {
45 break Ok(ProcessWaitResult::Completed(output?))
46 },
47 _ = sigtstp.recv() => {
48 break Ok(ProcessWaitResult::Stopped)
49 },
50 _ = sigchld.recv() => {
51 if sys::signal::poll_for_stopped_children()? {
52 break Ok(ProcessWaitResult::Stopped);
53 }
54 },
55 _ = sys::signal::await_ctrl_c() => {
56 },
60 }
61 }
62 }
63
64 pub(crate) fn poll(&mut self) -> Option<Result<std::process::Output, error::Error>> {
65 let checkable_future = &mut self.exec_future;
66 checkable_future
67 .now_or_never()
68 .map(|result| result.map_err(Into::into))
69 }
70}
71
72pub enum ProcessWaitResult {
74 Completed(std::process::Output),
76 Stopped,
78}