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}