summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Kettmeir <crowlkats@toaxl.com>2022-05-11 07:59:39 +0200
committerGitHub <noreply@github.com>2022-05-11 07:59:39 +0200
commitb67f874b3fb172168997be410b1d5e3c3109c763 (patch)
treeab24a5ffe1fd7bb54a43e9a6b3e4b13fa153cfe7
parente3f4b02f48c1e1b1e70cb9237126b9bc5d9720e3 (diff)
feat(runtime/spawn): add `AbortSignal` support (#14538)
-rw-r--r--cli/dts/lib.deno.unstable.d.ts6
-rw-r--r--cli/tests/unit/spawn_test.ts (renamed from cli/tests/unit/command_test.ts)26
-rw-r--r--runtime/js/40_spawn.js12
3 files changed, 43 insertions, 1 deletions
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index fbbe165f9..ca437bf44 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -1346,6 +1346,12 @@ declare namespace Deno {
uid?: number;
/** Similar to `uid`, but sets the group ID of the child process. */
gid?: number;
+ /**
+ * An AbortSignal that allows closing the process using the corresponding
+ * AbortController by sending the process a SIGTERM signal.
+ * Not Supported by execSync.
+ */
+ signal?: AbortSignal;
/** Defaults to "null". */
stdin?: "piped" | "inherit" | "null";
diff --git a/cli/tests/unit/command_test.ts b/cli/tests/unit/spawn_test.ts
index 4b3d5bd11..c2c7fdbac 100644
--- a/cli/tests/unit/command_test.ts
+++ b/cli/tests/unit/spawn_test.ts
@@ -249,6 +249,32 @@ Deno.test(
);
Deno.test(
+ { permissions: { run: true, read: true } },
+ async function spawnAbort() {
+ const ac = new AbortController();
+ const child = Deno.spawnChild(Deno.execPath(), {
+ args: [
+ "eval",
+ "setTimeout(console.log, 1e8)",
+ ],
+ signal: ac.signal,
+ stdout: "null",
+ stderr: "null",
+ });
+ queueMicrotask(() => ac.abort());
+ const status = await child.status;
+ assertEquals(status.success, false);
+ if (Deno.build.os === "windows") {
+ assertEquals(status.code, 1);
+ assertEquals(status.signal, null);
+ } else {
+ assertEquals(status.success, false);
+ assertEquals(status.code, 143);
+ }
+ },
+);
+
+Deno.test(
{ permissions: { read: true, run: false } },
async function spawnPermissions() {
await assertRejects(async () => {
diff --git a/runtime/js/40_spawn.js b/runtime/js/40_spawn.js
index 3b609be8b..4f680c7e1 100644
--- a/runtime/js/40_spawn.js
+++ b/runtime/js/40_spawn.js
@@ -5,6 +5,7 @@
const core = window.Deno.core;
const { pathFromURL } = window.__bootstrap.util;
const { illegalConstructorKey } = window.__bootstrap.webUtil;
+ const { add, remove } = window.__bootstrap.abortSignal;
const {
ArrayPrototypeMap,
ObjectEntries,
@@ -26,6 +27,7 @@
stdin = "null",
stdout = "piped",
stderr = "piped",
+ signal = undefined,
} = {}) {
const child = core.opSync("op_spawn_child", {
cmd: pathFromURL(command),
@@ -39,7 +41,10 @@
stdout,
stderr,
});
- return new Child(illegalConstructorKey, child);
+ return new Child(illegalConstructorKey, {
+ ...child,
+ signal,
+ });
}
async function collectOutput(readableStream) {
@@ -91,6 +96,7 @@
}
constructor(key = null, {
+ signal,
rid,
pid,
stdinRid,
@@ -119,8 +125,12 @@
this.#stderr = readableStreamForRid(stderrRid);
}
+ const onAbort = () => this.kill("SIGTERM");
+ signal?.[add](onAbort);
+
this.#status = core.opAsync("op_spawn_wait", this.#rid).then((res) => {
this.#rid = null;
+ signal?.[remove](onAbort);
return res;
});
}