waitpid_any/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! `waitpid(2)` but for arbitrary non-child processes.
//!
//! [`waitpid(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html) can only
//! be used to wait for direct child processes, or it fails immediately.
//!
//! This crate provides a extension to wait for the exit of any process, not necessarily child
//! processes. Due to platform limitations, the exit reason and status codes still cannot be
//! retrieved.
//!
//! ## Implementation details
//!
//! - On Linux, [`pidfd_open(2)`](https://man7.org/linux/man-pages/man2/pidfd_open.2.html) and
//!   [`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html) are used. Thus only Linux 5.3
//!   or later is supported.
//! - On Windows,
//!   [`OpenProcess`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess)
//!   and
//!   [`WaitForSingleObject`](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject)
//!   are used.
//! - On *BSD, including macOS,
//!   [`kqueue(2)`](https://man.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2) is used.
//! - Other platforms are not supported currently.
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
use std::io::Result;
use std::time::Duration;

#[cfg(target_os = "linux")]
#[path = "./linux.rs"]
mod imp;

#[cfg(any(
    target_os = "freebsd",
    target_os = "macos",
    target_os = "netbsd",
    target_os = "openbsd",
))]
#[path = "./bsd.rs"]
mod imp;

#[cfg(windows)]
#[path = "./windows.rs"]
mod imp;

#[cfg(test)]
mod tests;

/// A locked handle to a process.
///
/// See [`WaitHandle::open`] for more details.
#[derive(Debug)]
#[must_use = "`WaitHandle` does nothing unless you `wait` it"]
pub struct WaitHandle(imp::WaitHandle);

impl WaitHandle {
    /// Open an handle to the process with given PID.
    ///
    /// The opened handle always points to the same process entity, thus preventing race condition
    /// caused by PID reusing.
    ///
    /// # Errors
    ///
    /// Fails when the underlying syscall fails.
    ///
    /// # Caveats
    ///
    /// 1. PID itself does not own any resource in most platforms. Thus there is still a race
    ///    condition when the process pointed by the original PID is dead, reaped, and recycled all
    ///    before calling to this function. This is generally unavoidable. But you can try to
    ///    `open` the PID as soon as possible, before any potential `wait` operations, to mitigate
    ///    the issue.
    /// 2. If the given PID does not exists, it returns `ESRCH` on *NIX immediately. This can
    ///    also happen if the process is exited and reaped before this call. You may want to
    ///    regards this case as a successful wait, but the decision is up to you.
    pub fn open(pid: i32) -> Result<Self> {
        Ok(Self(imp::open(pid)?))
    }

    /// Blocks until the target process exits.
    ///
    /// Once the the target process exits, all following calls return `Ok(())` immediately.
    ///
    /// # Errors
    ///
    /// Fails when the underlying syscall fails. For *NIX platforms, `EINTR` may be returned in
    /// case of signals.
    pub fn wait(&mut self) -> Result<()> {
        imp::wait(&mut self.0, None)?.expect("no timeout");
        Ok(())
    }

    /// Blocks until the target process exits, or timeout.
    ///
    /// If the process exited in time, `Ok(Some(()))` is returned immediately when the event
    /// triggers. If it is not exited in `timeout`, `Ok(None)` is returned.
    /// Once the the target process exits, all following calls return `Ok(Some(()))` immediately.
    ///
    /// # Errors
    ///
    /// Fails when the underlying syscall fails. For *NIX platforms, `EINTR` may be returned in
    /// case of signals.
    pub fn wait_timeout(&mut self, timeout: Duration) -> Result<Option<()>> {
        imp::wait(&mut self.0, Some(timeout))
    }
}