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 exec_future: WaitableChildProcess,
16 pid: Option<sys::process::ProcessId>,
18 pgid: Option<sys::process::ProcessId>,
20}
21
22impl ChildProcess {
23 pub fn new(
25 child: sys::process::Child,
26 pid: Option<sys::process::ProcessId>,
27 pgid: Option<sys::process::ProcessId>,
28 ) -> Self {
29 Self {
30 exec_future: Box::pin(child.wait_with_output()),
31 pid,
32 pgid,
33 }
34 }
35
36 pub const fn pid(&self) -> Option<sys::process::ProcessId> {
38 self.pid
39 }
40
41 pub const fn pgid(&self) -> Option<sys::process::ProcessId> {
43 self.pgid
44 }
45
46 pub async fn wait(&mut self) -> Result<ProcessWaitResult, error::Error> {
48 #[allow(unused_mut, reason = "only mutated on some platforms")]
49 let mut sigtstp = sys::signal::tstp_signal_listener()?;
50 #[allow(unused_mut, reason = "only mutated on some platforms")]
51 let mut sigchld = sys::signal::chld_signal_listener()?;
52
53 #[allow(clippy::ignored_unit_patterns)]
54 loop {
55 tokio::select! {
56 output = &mut self.exec_future => {
57 break Ok(ProcessWaitResult::Completed(output?))
58 },
59 _ = sigtstp.recv() => {
60 break Ok(ProcessWaitResult::Stopped)
61 },
62 _ = sigchld.recv() => {
63 if sys::signal::poll_for_stopped_children()? {
64 break Ok(ProcessWaitResult::Stopped);
65 }
66 },
67 _ = sys::signal::await_ctrl_c() => {
68 },
72 }
73 }
74 }
75
76 pub(crate) fn poll(&mut self) -> Option<Result<std::process::Output, error::Error>> {
77 let checkable_future = &mut self.exec_future;
78 checkable_future
79 .now_or_never()
80 .map(|result| result.map_err(Into::into))
81 }
82}
83
84pub enum ProcessWaitResult {
86 Completed(std::process::Output),
88 Stopped,
90}