use crate::process_handle::ProcessHandle;
use crate::{OutputStream, async_drop};
use std::ops::{Deref, DerefMut};
use std::time::Duration;
#[derive(Debug)]
pub struct TerminateOnDrop<Stdout: OutputStream, Stderr: OutputStream = Stdout> {
pub(crate) process_handle: ProcessHandle<Stdout, Stderr>,
pub(crate) interrupt_timeout: Duration,
pub(crate) terminate_timeout: Duration,
}
impl<Stdout, Stderr> Deref for TerminateOnDrop<Stdout, Stderr>
where
Stdout: OutputStream,
Stderr: OutputStream,
{
type Target = ProcessHandle<Stdout, Stderr>;
fn deref(&self) -> &Self::Target {
&self.process_handle
}
}
impl<Stdout, Stderr> DerefMut for TerminateOnDrop<Stdout, Stderr>
where
Stdout: OutputStream,
Stderr: OutputStream,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.process_handle
}
}
impl<Stdout, Stderr> Drop for TerminateOnDrop<Stdout, Stderr>
where
Stdout: OutputStream,
Stderr: OutputStream,
{
fn drop(&mut self) {
async_drop::run_future(async {
match self.process_handle.is_running() {
crate::RunningState::Terminated(_) => {
tracing::debug!(
process = %self.process_handle.name,
"Process already terminated"
);
self.process_handle.must_not_be_terminated();
return;
}
crate::RunningState::Running => {}
crate::RunningState::Uncertain(err) => {
tracing::warn!(
process = %self.process_handle.name,
error = %err,
"Could not determine process state during drop; attempting best-effort termination"
);
}
}
tracing::debug!(process = %self.process_handle.name, "Terminating process");
match self
.process_handle
.terminate(self.interrupt_timeout, self.terminate_timeout)
.await
{
Ok(exit_status) => {
tracing::debug!(
process = %self.process_handle.name,
?exit_status,
"Successfully terminated process"
);
}
Err(err) => {
tracing::error!(
process = %self.process_handle.name,
error = %err,
"Failed to terminate process during drop"
);
}
}
});
}
}