waitpid_any/
lib.rs

1//! `waitpid(2)` but for arbitrary non-child processes.
2//!
3//! [`waitpid(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html) can only
4//! be used to wait for direct child processes, or it fails immediately.
5//!
6//! This crate provides a extension to wait for the exit of any process, not necessarily child
7//! processes. Due to platform limitations, the exit reason and status codes still cannot be
8//! retrieved.
9//!
10//! ## Implementation details
11//!
12//! - On Linux, [`pidfd_open(2)`](https://man7.org/linux/man-pages/man2/pidfd_open.2.html) and
13//!   [`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html) are used. Thus only Linux 5.3
14//!   or later is supported.
15//! - On Windows,
16//!   [`OpenProcess`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess)
17//!   and
18//!   [`WaitForSingleObject`](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject)
19//!   are used.
20//! - On *BSD, including macOS,
21//!   [`kqueue(2)`](https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2) is used.
22//! - Other platforms are not supported currently.
23#![warn(missing_debug_implementations)]
24#![warn(missing_docs)]
25use std::io::Result;
26use std::time::Duration;
27
28#[cfg(target_os = "linux")]
29#[path = "./linux.rs"]
30mod imp;
31
32#[cfg(any(
33    target_os = "freebsd",
34    target_os = "macos",
35    target_os = "netbsd",
36    target_os = "openbsd",
37))]
38#[path = "./bsd.rs"]
39mod imp;
40
41#[cfg(windows)]
42#[path = "./windows.rs"]
43mod imp;
44
45#[cfg(test)]
46mod tests;
47
48/// A locked handle to a process.
49///
50/// See [`WaitHandle::open`] for more details.
51#[derive(Debug)]
52#[must_use = "`WaitHandle` does nothing unless you `wait` it"]
53pub struct WaitHandle(imp::WaitHandle);
54
55impl WaitHandle {
56    /// Open an handle to the process with given PID.
57    ///
58    /// The opened handle always points to the same process entity, thus preventing race condition
59    /// caused by PID reusing.
60    ///
61    /// # Errors
62    ///
63    /// Fails when the underlying syscall fails.
64    ///
65    /// # Caveats
66    ///
67    /// 1. PID itself does not own any resource in most platforms. Thus there is still a race
68    ///    condition when the process pointed by the original PID is dead, reaped, and recycled all
69    ///    before calling to this function. This is generally unavoidable. But you can try to
70    ///    `open` the PID as soon as possible, before any potential `wait` operations, to mitigate
71    ///    the issue.
72    /// 2. If the given PID does not exists, it returns `ESRCH` on *NIX immediately. This can
73    ///    also happen if the process is exited and reaped before this call. You may want to
74    ///    regards this case as a successful wait, but the decision is up to you.
75    pub fn open(pid: i32) -> Result<Self> {
76        Ok(Self(imp::open(pid)?))
77    }
78
79    /// Blocks until the target process exits.
80    ///
81    /// Once the the target process exits, all following calls return `Ok(())` immediately.
82    ///
83    /// # Errors
84    ///
85    /// Fails when the underlying syscall fails. For *NIX platforms, `EINTR` may be returned in
86    /// case of signals.
87    // WAIT: https://github.com/rust-lang/rust-clippy/issues/11436
88    #[allow(clippy::missing_panics_doc)]
89    pub fn wait(&mut self) -> Result<()> {
90        imp::wait(&mut self.0, None)?.expect("no timeout");
91        Ok(())
92    }
93
94    /// Blocks until the target process exits, or timeout.
95    ///
96    /// If the process exited in time, `Ok(Some(()))` is returned immediately when the event
97    /// triggers. If it is not exited in `timeout`, `Ok(None)` is returned.
98    /// Once the the target process exits, all following calls return `Ok(Some(()))` immediately.
99    ///
100    /// # Errors
101    ///
102    /// Fails when the underlying syscall fails. For *NIX platforms, `EINTR` may be returned in
103    /// case of signals.
104    pub fn wait_timeout(&mut self, timeout: Duration) -> Result<Option<()>> {
105        imp::wait(&mut self.0, Some(timeout))
106    }
107}