summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert Belder <bertbelder@gmail.com>2021-01-13 20:52:12 -0800
committerBert Belder <bertbelder@gmail.com>2021-01-14 10:26:59 -0800
commit979d71c883ee76c5e1246c5ad02f2791b745bef6 (patch)
tree97e6212460238ed14fa7cf7504f1c70cd311acbb
parentb358426eeae1118e73a5420fbe1b3becdb5371a1 (diff)
refactor: make Process#kill() throw sensible errors on Windows (#9111)
Previously, calling `Process#kill()` after the process had exited would sometimes throw a `TypeError` on Windows. After this patch, it will throw `NotFound` instead, just like other platforms. This patch also fixes flakiness of the `runKillAfterStatus` test on Windows.
-rw-r--r--cli/tests/unit/process_test.ts24
-rw-r--r--runtime/ops/process.rs72
2 files changed, 48 insertions, 48 deletions
diff --git a/cli/tests/unit/process_test.ts b/cli/tests/unit/process_test.ts
index 1eaa3c3b3..9bb4d7fc2 100644
--- a/cli/tests/unit/process_test.ts
+++ b/cli/tests/unit/process_test.ts
@@ -434,16 +434,20 @@ unitTest(
});
await p.status();
- // On Windows the underlying Rust API returns `ERROR_ACCESS_DENIED`,
- // which serves kind of as a catch all error code. More specific
- // error codes do exist, e.g. `ERROR_WAIT_NO_CHILDREN`; it's unclear
- // why they're not returned.
- const expectedErrorType = Deno.build.os === "windows"
- ? Deno.errors.PermissionDenied
- : Deno.errors.NotFound;
- assertThrows(
- () => p.kill(Deno.Signal.SIGTERM),
- expectedErrorType,
+ let error = null;
+ try {
+ p.kill(Deno.Signal.SIGTERM);
+ } catch (e) {
+ error = e;
+ }
+
+ assert(
+ error instanceof Deno.errors.NotFound ||
+ // On Windows, the underlying Windows API may return
+ // `ERROR_ACCESS_DENIED` when the process has exited, but hasn't been
+ // completely cleaned up yet and its `pid` is still valid.
+ (Deno.build.os === "windows" &&
+ error instanceof Deno.errors.PermissionDenied),
);
p.close();
diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs
index 63e22b601..412d21ef2 100644
--- a/runtime/ops/process.rs
+++ b/runtime/ops/process.rs
@@ -219,23 +219,6 @@ async fn op_run_status(
}))
}
-#[cfg(not(unix))]
-const SIGINT: i32 = 2;
-#[cfg(not(unix))]
-const SIGKILL: i32 = 9;
-#[cfg(not(unix))]
-const SIGTERM: i32 = 15;
-
-#[cfg(not(unix))]
-use winapi::{
- shared::minwindef::DWORD,
- um::{
- handleapi::CloseHandle,
- processthreadsapi::{OpenProcess, TerminateProcess},
- winnt::PROCESS_TERMINATE,
- },
-};
-
#[cfg(unix)]
pub fn kill(pid: i32, signo: i32) -> Result<(), AnyError> {
use nix::sys::signal::{kill as unix_kill, Signal};
@@ -248,30 +231,43 @@ pub fn kill(pid: i32, signo: i32) -> Result<(), AnyError> {
#[cfg(not(unix))]
pub fn kill(pid: i32, signal: i32) -> Result<(), AnyError> {
use std::io::Error;
- match signal {
- SIGINT | SIGKILL | SIGTERM => {
- if pid <= 0 {
- return Err(type_error("unsupported pid"));
- }
- unsafe {
- let handle = OpenProcess(PROCESS_TERMINATE, 0, pid as DWORD);
- if handle.is_null() {
- return Err(Error::last_os_error().into());
- }
- if TerminateProcess(handle, 1) == 0 {
- CloseHandle(handle);
- return Err(Error::last_os_error().into());
- }
- if CloseHandle(handle) == 0 {
- return Err(Error::last_os_error().into());
- }
+ use std::io::ErrorKind::NotFound;
+ use winapi::shared::minwindef::DWORD;
+ use winapi::shared::minwindef::FALSE;
+ use winapi::shared::minwindef::TRUE;
+ use winapi::shared::winerror::ERROR_INVALID_PARAMETER;
+ use winapi::um::errhandlingapi::GetLastError;
+ use winapi::um::handleapi::CloseHandle;
+ use winapi::um::processthreadsapi::OpenProcess;
+ use winapi::um::processthreadsapi::TerminateProcess;
+ use winapi::um::winnt::PROCESS_TERMINATE;
+
+ const SIGINT: i32 = 2;
+ const SIGKILL: i32 = 9;
+ const SIGTERM: i32 = 15;
+
+ if !matches!(signal, SIGINT | SIGKILL | SIGTERM) {
+ Err(type_error("unsupported signal"))
+ } else if pid <= 0 {
+ Err(type_error("unsupported pid"))
+ } else {
+ let handle = unsafe { OpenProcess(PROCESS_TERMINATE, FALSE, pid as DWORD) };
+ if handle.is_null() {
+ let err = match unsafe { GetLastError() } {
+ ERROR_INVALID_PARAMETER => Error::from(NotFound), // Invalid `pid`.
+ errno => Error::from_raw_os_error(errno as i32),
+ };
+ Err(err.into())
+ } else {
+ let r = unsafe { TerminateProcess(handle, 1) };
+ unsafe { CloseHandle(handle) };
+ match r {
+ FALSE => Err(Error::last_os_error().into()),
+ TRUE => Ok(()),
+ _ => unreachable!(),
}
}
- _ => {
- return Err(type_error("unsupported signal"));
- }
}
- Ok(())
}
#[derive(Deserialize)]