tokio_process_terminate/
lib.rs

1//! <!-- cargo-sync-readme -->
2//! Extensions to `tokio::process::Child` to terminate processes.
3//!
4//! ```rust
5//! use tokio::process::Command;
6//! use tokio_process_terminate::TerminateExt;
7//!
8//! #[tokio::main]
9//! async fn main() {
10//! #   #[cfg(unix)]
11//! #   {
12//!     let mut command = Command::new("sleep")
13//!         .arg("10")
14//!         .spawn()
15//!         .unwrap();
16//!     tokio::time::sleep(std::time::Duration::from_secs(1)).await;
17//!     let exit = command.terminate_wait().await.unwrap();
18//!     dbg!(exit);
19//!     let code = exit.code();
20//!     // On Unix, code should be `None` if the process was terminated by a signal.
21//!     assert!(code.is_none());
22//! #   }
23//! }
24//! ```
25
26use std::{process::ExitStatus, time::Duration};
27
28#[async_trait::async_trait]
29pub trait TerminateExt {
30    /// Send a signal to the process to terminate it.
31    ///
32    /// On Unix, this sends a SIGTERM signal to the process.
33    /// On Windows, this sends a CTRL_C_EVENT to the process.
34    fn terminate(&mut self);
35
36    #[doc(hidden)]
37    async fn _wait(&mut self) -> std::io::Result<ExitStatus>;
38    #[doc(hidden)]
39    async fn _kill(&mut self) -> std::io::Result<()>;
40
41    /// Terminate the process and wait for it to exit.
42    async fn terminate_wait(&mut self) -> std::io::Result<ExitStatus> {
43        self.terminate();
44        self._wait().await
45    }
46
47    /// Terminate the process and wait for it to exit, or kill it after a timeout.
48    ///
49    /// If the process exits before the timeout, the exit status is returned.
50    /// If the timeout elapses before the process exits, it is killed and `None` is returned.
51    async fn terminate_timeout(
52        &mut self,
53        timeout: Duration,
54    ) -> std::io::Result<Option<ExitStatus>> {
55        self.terminate();
56        match tokio::time::timeout(timeout, self._wait()).await {
57            Ok(result) => result.map(Some),
58            Err(_) => self._kill().await.map(|_| None),
59        }
60    }
61}
62
63#[cfg(unix)]
64pub mod unix;
65
66#[cfg(windows)]
67mod windows;