Skip to content

Commit a0a29f6

Browse files
committed
Runtime process termination on Windows with timeout
1 parent cfb790e commit a0a29f6

File tree

1 file changed

+39
-35
lines changed

1 file changed

+39
-35
lines changed

utils/process/src/lib.rs

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use anyhow::{anyhow, Result};
22
use derive_more::Display;
33
use futures::channel::oneshot::channel;
4+
use futures::future::{AbortHandle, Abortable};
45
use shared_child::SharedChild;
56
use std::process::Command;
67
use std::sync::Arc;
@@ -11,17 +12,14 @@ use std::time::Duration;
1112
pub mod lock;
1213

1314
#[cfg(unix)]
14-
use {
15-
futures::future::{AbortHandle, Abortable},
16-
shared_child::unix::SharedChildExt,
17-
};
15+
use shared_child::unix::SharedChildExt;
1816

1917
#[cfg(windows)]
20-
use {
21-
winapi::um::handleapi::CloseHandle,
22-
winapi::um::processthreadsapi::OpenProcess,
23-
winapi::um::wincon::{GenerateConsoleCtrlEvent, CTRL_BREAK_EVENT},
24-
winapi::um::winnt::{PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, SYNCHRONIZE},
18+
use std::process::ExitStatus;
19+
#[cfg(windows)]
20+
use winapi::um::{
21+
errhandlingapi,
22+
wincon::{GenerateConsoleCtrlEvent, CTRL_BREAK_EVENT},
2523
};
2624

2725
pub trait ProcessGroupExt<T> {
@@ -125,36 +123,28 @@ impl ProcessHandle {
125123
}
126124

127125
#[cfg(windows)]
128-
pub async fn terminate(&self, _timeout: Duration) -> Result<()> {
126+
pub async fn terminate(&self, timeout: Duration) -> Result<()> {
127+
use anyhow::bail;
128+
129129
let process = self.process.clone();
130-
let process_pid = process.id();
130+
let (abort_handle, abort_registration) = AbortHandle::new_pair();
131+
let _process_pid = process.id();
131132

132-
unsafe {
133-
let process_handle = OpenProcess(
134-
PROCESS_QUERY_INFORMATION | SYNCHRONIZE | PROCESS_TERMINATE,
135-
0,
136-
process_pid,
137-
);
138-
139-
if process_handle.is_null() {
140-
return Err(anyhow!(
141-
"Unable to open the process. Error code: {}",
142-
winapi::um::errhandlingapi::GetLastError()
143-
));
144-
}
145-
146-
let event_result = GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, process_pid);
147-
148-
if event_result == 0 {
149-
return Err(anyhow!(
150-
"Unable to send CTRL+C event to the process. Error code: {}",
151-
winapi::um::errhandlingapi::GetLastError()
152-
));
153-
};
154-
CloseHandle(process_handle);
133+
tokio::task::spawn_local(async move {
134+
tokio::time::sleep(timeout).await;
135+
abort_handle.abort();
136+
});
137+
138+
if let Ok(Err(err)) = Abortable::new(
139+
async { unsafe { ctrl_break_process(process) } },
140+
abort_registration,
141+
)
142+
.await
143+
{
144+
bail!(err.context("Failed to CTRL-BREAK process"));
155145
}
156146

157-
Ok(())
147+
self.check_if_running()
158148
}
159149

160150
pub fn check_if_running(&self) -> Result<()> {
@@ -200,3 +190,17 @@ impl ProcessHandle {
200190
receiver.await.unwrap()
201191
}
202192
}
193+
194+
#[cfg(windows)]
195+
unsafe fn ctrl_break_process(process: Arc<SharedChild>) -> Result<ExitStatus> {
196+
let process_pid = process.id();
197+
let event_result = GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, process_pid);
198+
199+
if event_result == 0 {
200+
return Err(anyhow!(
201+
"Unable to send CTRL-BREAK event to the process. Error code: {}",
202+
errhandlingapi::GetLastError()
203+
));
204+
};
205+
Ok(process.wait()?)
206+
}

0 commit comments

Comments
 (0)