async_process/reaper/
mod.rs

1//! The underlying system reaper.
2//!
3//! There are two backends:
4//!
5//! - signal, which waits for SIGCHLD.
6//! - wait, which waits directly on a process handle.
7//!
8//! "wait" is preferred, but is not available on all supported Linuxes. So we
9//! test to see if pidfd is supported first. If it is, we use wait. If not, we use
10//! signal.
11
12#![allow(irrefutable_let_patterns)]
13
14/// Enable the waiting reaper.
15#[cfg(any(windows, target_os = "linux"))]
16macro_rules! cfg_wait {
17    ($($tt:tt)*) => {$($tt)*};
18}
19
20/// Enable the waiting reaper.
21#[cfg(not(any(windows, target_os = "linux")))]
22macro_rules! cfg_wait {
23    ($($tt:tt)*) => {};
24}
25
26/// Enable signals.
27#[cfg(not(windows))]
28macro_rules! cfg_signal {
29    ($($tt:tt)*) => {$($tt)*};
30}
31
32/// Enable signals.
33#[cfg(windows)]
34macro_rules! cfg_signal {
35    ($($tt:tt)*) => {};
36}
37
38cfg_wait! {
39    mod wait;
40}
41
42cfg_signal! {
43    mod signal;
44}
45
46use std::io;
47use std::sync::Mutex;
48
49/// The underlying system reaper.
50pub(crate) enum Reaper {
51    #[cfg(any(windows, target_os = "linux"))]
52    /// The reaper based on the wait backend.
53    Wait(wait::Reaper),
54
55    /// The reaper based on the signal backend.
56    #[cfg(not(windows))]
57    Signal(signal::Reaper),
58}
59
60/// The wrapper around a child.
61pub(crate) enum ChildGuard {
62    #[cfg(any(windows, target_os = "linux"))]
63    /// The child guard based on the wait backend.
64    Wait(wait::ChildGuard),
65
66    /// The child guard based on the signal backend.
67    #[cfg(not(windows))]
68    Signal(signal::ChildGuard),
69}
70
71/// A lock on the reaper.
72pub(crate) enum Lock {
73    #[cfg(any(windows, target_os = "linux"))]
74    /// The wait-based reaper needs no lock.
75    Wait,
76
77    /// The lock for the signal-based reaper.
78    #[cfg(not(windows))]
79    Signal(signal::Lock),
80}
81
82impl Reaper {
83    /// Create a new reaper.
84    pub(crate) fn new() -> Self {
85        cfg_wait! {
86            if wait::available() && !cfg!(async_process_force_signal_backend) {
87                return Self::Wait(wait::Reaper::new());
88            }
89        }
90
91        // Return the signal-based reaper.
92        cfg_signal! {
93            return Self::Signal(signal::Reaper::new());
94        }
95
96        #[allow(unreachable_code)]
97        {
98            panic!("neither the signal backend nor the waiter backend is available")
99        }
100    }
101
102    /// Lock the driver thread.
103    ///
104    /// This makes it so only one thread can reap at once.
105    pub(crate) async fn lock(&'static self) -> Lock {
106        cfg_wait! {
107            if let Self::Wait(_this) = self {
108                // No locking needed.
109                return Lock::Wait;
110            }
111        }
112
113        cfg_signal! {
114            if let Self::Signal(this) = self {
115                // We need to lock.
116                return Lock::Signal(this.lock().await);
117            }
118        }
119
120        unreachable!()
121    }
122
123    /// Reap zombie processes forever.
124    pub(crate) async fn reap(&'static self, lock: Lock) -> ! {
125        cfg_wait! {
126            if let (Self::Wait(this), Lock::Wait) = (self, &lock) {
127                this.reap().await;
128            }
129        }
130
131        cfg_signal! {
132            if let (Self::Signal(this), Lock::Signal(lock)) = (self, lock) {
133                this.reap(lock).await;
134            }
135        }
136
137        unreachable!()
138    }
139
140    /// Register a child into this reaper.
141    pub(crate) fn register(&'static self, child: std::process::Child) -> io::Result<ChildGuard> {
142        cfg_wait! {
143            if let Self::Wait(this) = self {
144                return this.register(child).map(ChildGuard::Wait);
145            }
146        }
147
148        cfg_signal! {
149            if let Self::Signal(this) = self {
150                return this.register(child).map(ChildGuard::Signal);
151            }
152        }
153
154        unreachable!()
155    }
156
157    /// Wait for the inner child to complete.
158    pub(crate) async fn status(
159        &'static self,
160        child: &Mutex<crate::ChildGuard>,
161    ) -> io::Result<std::process::ExitStatus> {
162        cfg_wait! {
163            if let Self::Wait(this) = self {
164                return this.status(child).await;
165            }
166        }
167
168        cfg_signal! {
169            if let Self::Signal(this) = self {
170                return this.status(child).await;
171            }
172        }
173
174        unreachable!()
175    }
176
177    /// Do we have any registered zombie processes?
178    pub(crate) fn has_zombies(&'static self) -> bool {
179        cfg_wait! {
180            if let Self::Wait(this) = self {
181                return this.has_zombies();
182            }
183        }
184
185        cfg_signal! {
186            if let Self::Signal(this) = self {
187                return this.has_zombies();
188            }
189        }
190
191        unreachable!()
192    }
193}
194
195impl ChildGuard {
196    /// Get a reference to the inner process.
197    pub(crate) fn get_mut(&mut self) -> &mut std::process::Child {
198        cfg_wait! {
199            if let Self::Wait(this) = self {
200                return this.get_mut();
201            }
202        }
203
204        cfg_signal! {
205            if let Self::Signal(this) = self {
206                return this.get_mut();
207            }
208        }
209
210        unreachable!()
211    }
212
213    /// Start reaping this child process.
214    pub(crate) fn reap(&mut self, reaper: &'static Reaper) {
215        cfg_wait! {
216            if let (Self::Wait(this), Reaper::Wait(reaper)) = (&mut *self, reaper) {
217                this.reap(reaper);
218                return;
219            }
220        }
221
222        cfg_signal! {
223            if let (Self::Signal(this), Reaper::Signal(reaper)) = (self, reaper) {
224                this.reap(reaper);
225                return;
226            }
227        }
228
229        unreachable!()
230    }
231}