summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2022-07-22 18:07:20 +0200
committerGitHub <noreply@github.com>2022-07-22 18:07:20 +0200
commit72199303d899b8ddf2ff46ed11bd513ed9cc47e2 (patch)
treedc44930e29a070de4d0cf535d2958031c27345d1
parent4db650ddd57b85475d71c0b9fc84d37becab9d6a (diff)
fix: Child.unref() unrefs stdio streams properly (#15275)
-rw-r--r--cli/tests/unit/spawn_test.ts29
-rw-r--r--runtime/js/40_spawn.js5
2 files changed, 32 insertions, 2 deletions
diff --git a/cli/tests/unit/spawn_test.ts b/cli/tests/unit/spawn_test.ts
index 42df23b1f..51664e3d1 100644
--- a/cli/tests/unit/spawn_test.ts
+++ b/cli/tests/unit/spawn_test.ts
@@ -723,8 +723,29 @@ Deno.test(
const program = `
const child = await Deno.spawnChild(Deno.execPath(), {
cwd: Deno.args[0],
+ stdout: "piped",
args: ["run", "-A", "--unstable", Deno.args[1]],
});
+const readable = child.stdout.pipeThrough(new TextDecoderStream());
+const reader = readable.getReader();
+// set up an interval that will end after reading a few messages from stdout,
+// to verify that stdio streams are properly unrefed
+let count = 0;
+let interval;
+interval = setInterval(async () => {
+ count += 1;
+ if (count > 10) {
+ clearInterval(interval);
+ console.log("cleared interval");
+ }
+ const res = await reader.read();
+ if (res.done) {
+ throw new Error("stream shouldn't be done");
+ }
+ if (res.value.trim() != "hello from interval") {
+ throw new Error("invalid message received");
+ }
+}, 120);
console.log("spawned pid", child.pid);
child.unref();
`;
@@ -740,15 +761,19 @@ setInterval(() => {
// In this subprocess we are spawning another subprocess which has
// an infite interval set. Following call would never resolve unless
// child process gets unrefed.
- const { success, stdout } = await Deno.spawn(Deno.execPath(), {
+ const { success, stdout, stderr } = await Deno.spawn(Deno.execPath(), {
cwd,
args: ["run", "-A", "--unstable", programFile, cwd, childProgramFile],
});
assert(success);
const stdoutText = new TextDecoder().decode(stdout);
- const pidStr = stdoutText.split(" ").at(-1);
+ const stderrText = new TextDecoder().decode(stderr);
+ assert(stderrText.length == 0);
+ const [line1, line2] = stdoutText.split("\n");
+ const pidStr = line1.split(" ").at(-1);
assert(pidStr);
+ assertEquals(line2, "cleared interval");
const pid = Number.parseInt(pidStr, 10);
await Deno.remove(cwd, { recursive: true });
// Child process should have been killed when parent process exits.
diff --git a/runtime/js/40_spawn.js b/runtime/js/40_spawn.js
index a00b8cfda..87db84f4b 100644
--- a/runtime/js/40_spawn.js
+++ b/runtime/js/40_spawn.js
@@ -75,6 +75,7 @@
class Child {
#rid;
#waitPromiseId;
+ #unrefed = false;
#pid;
get pid() {
@@ -132,6 +133,7 @@
this.#stdoutRid = stdoutRid;
this.#stdout = readableStreamForRid(stdoutRid, (promise) => {
this.#stdoutPromiseId = promise[promiseIdSymbol];
+ if (this.#unrefed) core.unrefOp(this.#stdoutPromiseId);
});
}
@@ -139,6 +141,7 @@
this.#stderrRid = stderrRid;
this.#stderr = readableStreamForRid(stderrRid, (promise) => {
this.#stderrPromiseId = promise[promiseIdSymbol];
+ if (this.#unrefed) core.unrefOp(this.#stderrPromiseId);
});
}
@@ -204,12 +207,14 @@
}
ref() {
+ this.#unrefed = false;
core.refOp(this.#waitPromiseId);
if (this.#stdoutPromiseId) core.refOp(this.#stdoutPromiseId);
if (this.#stderrPromiseId) core.refOp(this.#stderrPromiseId);
}
unref() {
+ this.#unrefed = true;
core.unrefOp(this.#waitPromiseId);
if (this.#stdoutPromiseId) core.unrefOp(this.#stdoutPromiseId);
if (this.#stderrPromiseId) core.unrefOp(this.#stderrPromiseId);