Skip to main content

tokio_process_tools/
terminate_on_drop.rs

1use crate::process_handle::ProcessHandle;
2use crate::process_handle::termination::GracefulTimeouts;
3use crate::{OutputStream, async_drop};
4use std::ops::{Deref, DerefMut};
5
6/// A wrapper that automatically terminates a process when dropped.
7///
8/// # Safety Requirements
9///
10/// **WARNING**: This type requires a multithreaded tokio runtime to function correctly!
11///
12/// # Usage Guidelines
13///
14/// This type should only be used when:
15/// - Your code is running in a multithreaded tokio runtime.
16/// - Automatic process cleanup on drop is absolutely necessary.
17///
18/// # Recommended Alternatives
19///
20/// Instead of relying on automatic termination, prefer these safer approaches:
21/// 1. Manual process termination using [`ProcessHandle::terminate`]
22/// 2. Awaiting process completion using [`ProcessHandle::wait_for_completion`]
23/// 3. Awaiting process completion or performing an explicit termination using
24///    [`ProcessHandle::wait_for_completion_or_terminate`]
25///
26/// # Implementation Details
27///
28/// The drop implementation tries to terminate the process if it was neither awaited nor
29/// terminated before being dropped. If checking the current process state fails, it still attempts
30/// best-effort termination. If termination fails, a panic is raised.
31#[derive(Debug)]
32pub struct TerminateOnDrop<Stdout: OutputStream, Stderr: OutputStream = Stdout> {
33    pub(crate) process_handle: ProcessHandle<Stdout, Stderr>,
34    pub(crate) timeouts: GracefulTimeouts,
35}
36
37impl<Stdout, Stderr> Deref for TerminateOnDrop<Stdout, Stderr>
38where
39    Stdout: OutputStream,
40    Stderr: OutputStream,
41{
42    type Target = ProcessHandle<Stdout, Stderr>;
43
44    fn deref(&self) -> &Self::Target {
45        &self.process_handle
46    }
47}
48
49impl<Stdout, Stderr> DerefMut for TerminateOnDrop<Stdout, Stderr>
50where
51    Stdout: OutputStream,
52    Stderr: OutputStream,
53{
54    fn deref_mut(&mut self) -> &mut Self::Target {
55        &mut self.process_handle
56    }
57}
58
59impl<Stdout, Stderr> Drop for TerminateOnDrop<Stdout, Stderr>
60where
61    Stdout: OutputStream,
62    Stderr: OutputStream,
63{
64    fn drop(&mut self) {
65        async_drop::run_future(async {
66            match self.process_handle.is_running() {
67                crate::RunningState::Terminated(_) => {
68                    tracing::debug!(
69                        process = %self.process_handle.name,
70                        "Process already terminated"
71                    );
72                    // The child has exited; close the lifecycle so the inner ProcessHandle's
73                    // drop guards do not panic when this wrapper drops out of scope.
74                    self.process_handle.must_not_be_terminated();
75                    return;
76                }
77                crate::RunningState::Running => {}
78                crate::RunningState::Uncertain(err) => {
79                    tracing::warn!(
80                        process = %self.process_handle.name,
81                        error = %err,
82                        "Could not determine process state during drop; attempting best-effort termination"
83                    );
84                }
85            }
86
87            tracing::debug!(process = %self.process_handle.name, "Terminating process");
88            match self.process_handle.terminate(self.timeouts).await {
89                Ok(exit_status) => {
90                    tracing::debug!(
91                        process = %self.process_handle.name,
92                        ?exit_status,
93                        "Successfully terminated process"
94                    );
95                }
96                Err(err) => {
97                    tracing::error!(
98                        process = %self.process_handle.name,
99                        error = %err,
100                        "Failed to terminate process during drop"
101                    );
102                }
103            }
104        });
105    }
106}