diff options
author | Ali Hasani <a.hassssani@gmail.com> | 2020-05-17 21:41:24 +0430 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-17 19:11:24 +0200 |
commit | eddb916883901385233bea24313e2920fcac5388 (patch) | |
tree | 0dbde6a34037b1c2b2459077731cb9afdc5b3a35 | |
parent | a054250a2cd709f74a3c9984af0cb74b7adde3bd (diff) |
Implement Deno.kill for windows (#5347)
-rw-r--r-- | cli/js/lib.deno.unstable.d.ts | 2 | ||||
-rw-r--r-- | cli/js/ops/process.ts | 4 | ||||
-rw-r--r-- | cli/js/tests/process_test.ts | 110 | ||||
-rw-r--r-- | cli/signal.rs | 43 | ||||
-rw-r--r-- | std/http/server_test.ts | 12 |
5 files changed, 104 insertions, 67 deletions
diff --git a/cli/js/lib.deno.unstable.d.ts b/cli/js/lib.deno.unstable.d.ts index a166cb1c0..d34d86636 100644 --- a/cli/js/lib.deno.unstable.d.ts +++ b/cli/js/lib.deno.unstable.d.ts @@ -1120,8 +1120,6 @@ declare namespace Deno { * * Deno.kill(p.pid, Deno.Signal.SIGINT); * - * Throws Error (not yet implemented) on Windows - * * Requires `allow-run` permission. */ export function kill(pid: number, signo: number): void; diff --git a/cli/js/ops/process.ts b/cli/js/ops/process.ts index 4fba4d2de..39c6eb8b7 100644 --- a/cli/js/ops/process.ts +++ b/cli/js/ops/process.ts @@ -1,12 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. import { sendSync, sendAsync } from "./dispatch_json.ts"; import { assert } from "../util.ts"; -import { build } from "../build.ts"; export function kill(pid: number, signo: number): void { - if (build.os === "windows") { - throw new Error("Not yet implemented"); - } sendSync("op_kill", { pid, signo }); } diff --git a/cli/js/tests/process_test.ts b/cli/js/tests/process_test.ts index b70ce8307..1ea6f95b7 100644 --- a/cli/js/tests/process_test.ts +++ b/cli/js/tests/process_test.ts @@ -5,12 +5,20 @@ import { assertStrContains, unitTest, } from "./test_util.ts"; -const { kill, run, readFile, open, makeTempDir, writeFile } = Deno; +const { + kill, + run, + readFile, + open, + makeTempDir, + writeFile, + writeFileSync, +} = Deno; unitTest(function runPermissions(): void { let caughtError = false; try { - Deno.run({ cmd: ["python", "-c", "print('hello world')"] }); + run({ cmd: ["python", "-c", "print('hello world')"] }); } catch (e) { caughtError = true; assert(e instanceof Deno.errors.PermissionDenied); @@ -99,7 +107,7 @@ while True: pass `; - Deno.writeFileSync(`${cwd}/${pyProgramFile}.py`, enc.encode(pyProgram)); + writeFileSync(`${cwd}/${pyProgramFile}.py`, enc.encode(pyProgram)); const p = run({ cwd, cmd: ["python", `${pyProgramFile}.py`], @@ -108,7 +116,7 @@ while True: // Write the expected exit code *after* starting python. // This is how we verify that `run()` is actually asynchronous. const code = 84; - Deno.writeFileSync(`${cwd}/${exitCodeFile}`, enc.encode(`${code}`)); + writeFileSync(`${cwd}/${exitCodeFile}`, enc.encode(`${code}`)); const status = await p.status(); assertEquals(status.success, false); @@ -325,60 +333,54 @@ unitTest(function signalNumbers(): void { } }); -// Ignore signal tests on windows for now... -if (Deno.build.os !== "windows") { - unitTest(function killPermissions(): void { - let caughtError = false; - try { - // Unlike the other test cases, we don't have permission to spawn a - // subprocess we can safely kill. Instead we send SIGCONT to the current - // process - assuming that Deno does not have a special handler set for it - // and will just continue even if a signal is erroneously sent. - Deno.kill(Deno.pid, Deno.Signal.SIGCONT); - } catch (e) { - caughtError = true; - assert(e instanceof Deno.errors.PermissionDenied); - } - assert(caughtError); +unitTest(function killPermissions(): void { + let caughtError = false; + try { + // Unlike the other test cases, we don't have permission to spawn a + // subprocess we can safely kill. Instead we send SIGCONT to the current + // process - assuming that Deno does not have a special handler set for it + // and will just continue even if a signal is erroneously sent. + kill(Deno.pid, Deno.Signal.SIGCONT); + } catch (e) { + caughtError = true; + assert(e instanceof Deno.errors.PermissionDenied); + } + assert(caughtError); +}); + +unitTest({ perms: { run: true } }, async function killSuccess(): Promise<void> { + const p = run({ + cmd: ["python", "-c", "from time import sleep; sleep(10000)"], }); - unitTest({ perms: { run: true } }, async function killSuccess(): Promise< - void - > { - const p = run({ - cmd: ["python", "-c", "from time import sleep; sleep(10000)"], - }); + assertEquals(Deno.Signal.SIGINT, 2); + kill(p.pid, Deno.Signal.SIGINT); + const status = await p.status(); - assertEquals(Deno.Signal.SIGINT, 2); - kill(p.pid, Deno.Signal.SIGINT); - const status = await p.status(); + assertEquals(status.success, false); + // TODO(ry) On Linux, status.code is sometimes undefined and sometimes 1. + // The following assert is causing this test to be flaky. Investigate and + // re-enable when it can be made deterministic. + // assertEquals(status.code, 1); + // assertEquals(status.signal, Deno.Signal.SIGINT); + p.close(); +}); - assertEquals(status.success, false); - // TODO(ry) On Linux, status.code is sometimes undefined and sometimes 1. - // The following assert is causing this test to be flaky. Investigate and - // re-enable when it can be made deterministic. - // assertEquals(status.code, 1); - // assertEquals(status.signal, Deno.Signal.SIGINT); - p.close(); +unitTest({ perms: { run: true } }, function killFailed(): void { + const p = run({ + cmd: ["python", "-c", "from time import sleep; sleep(10000)"], }); + assert(!p.stdin); + assert(!p.stdout); - unitTest({ perms: { run: true } }, function killFailed(): void { - const p = run({ - cmd: ["python", "-c", "from time import sleep; sleep(10000)"], - }); - assert(!p.stdin); - assert(!p.stdout); - - let err; - try { - kill(p.pid, 12345); - } catch (e) { - err = e; - } - - assert(!!err); - assert(err instanceof TypeError); + let err; + try { + kill(p.pid, 12345); + } catch (e) { + err = e; + } + assert(!!err); + assert(err instanceof TypeError); - p.close(); - }); -} + p.close(); +}); diff --git a/cli/signal.rs b/cli/signal.rs index 6be588b84..f74f9ab26 100644 --- a/cli/signal.rs +++ b/cli/signal.rs @@ -1,6 +1,23 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use crate::op_error::OpError; +#[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<(), OpError> { use nix::sys::signal::{kill as unix_kill, Signal}; @@ -11,7 +28,29 @@ pub fn kill(pid: i32, signo: i32) -> Result<(), OpError> { } #[cfg(not(unix))] -pub fn kill(_pid: i32, _signal: i32) -> Result<(), OpError> { - // TODO: implement this for windows +pub fn kill(pid: i32, signal: i32) -> Result<(), OpError> { + match signal { + SIGINT | SIGKILL | SIGTERM => { + if pid <= 0 { + return Err(OpError::type_error("unsupported pid".to_string())); + } + unsafe { + let handle = OpenProcess(PROCESS_TERMINATE, 0, pid as DWORD); + if handle.is_null() { + return Err(OpError::from(std::io::Error::last_os_error())); + } + if TerminateProcess(handle, 1) == 0 { + CloseHandle(handle); + return Err(OpError::from(std::io::Error::last_os_error())); + } + if CloseHandle(handle) == 0 { + return Err(OpError::from(std::io::Error::last_os_error())); + } + } + } + _ => { + return Err(OpError::type_error("unsupported signal".to_string())); + } + } Ok(()) } diff --git a/std/http/server_test.ts b/std/http/server_test.ts index 807695c6b..0560f7f8d 100644 --- a/std/http/server_test.ts +++ b/std/http/server_test.ts @@ -351,12 +351,15 @@ test("requestBodyReaderWithTransferEncoding", async function (): Promise<void> { test({ name: "destroyed connection", - // FIXME(bartlomieju): hangs on windows, cause can't do `Deno.kill` - ignore: true, fn: async (): Promise<void> => { // Runs a simple server as another process const p = Deno.run({ - cmd: [Deno.execPath(), "--allow-net", "http/testdata/simple_server.ts"], + cmd: [ + Deno.execPath(), + "run", + "--allow-net", + "http/testdata/simple_server.ts", + ], stdout: "piped", }); @@ -392,13 +395,12 @@ test({ test({ name: "serveTLS", - // FIXME(bartlomieju): hangs on windows, cause can't do `Deno.kill` - ignore: true, fn: async (): Promise<void> => { // Runs a simple server as another process const p = Deno.run({ cmd: [ Deno.execPath(), + "run", "--allow-net", "--allow-read", "http/testdata/simple_https_server.ts", |