summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/io/12_io.js32
-rw-r--r--ext/node/polyfills/tty.js13
-rw-r--r--tests/integration/run_tests.rs29
-rw-r--r--tests/testdata/run/node_process_stdin_unref_with_pty.js14
4 files changed, 85 insertions, 3 deletions
diff --git a/ext/io/12_io.js b/ext/io/12_io.js
index c43117cef..094868371 100644
--- a/ext/io/12_io.js
+++ b/ext/io/12_io.js
@@ -9,6 +9,7 @@ import { op_set_raw } from "ext:core/ops";
const {
Uint8Array,
ArrayPrototypePush,
+ Symbol,
TypedArrayPrototypeSubarray,
TypedArrayPrototypeSet,
TypedArrayPrototypeGetByteLength,
@@ -181,9 +182,14 @@ const STDIN_RID = 0;
const STDOUT_RID = 1;
const STDERR_RID = 2;
+const REF = Symbol("REF");
+const UNREF = Symbol("UNREF");
+
class Stdin {
#rid = STDIN_RID;
+ #ref = true;
#readable;
+ #opPromise;
constructor() {
}
@@ -197,8 +203,14 @@ class Stdin {
return this.#rid;
}
- read(p) {
- return read(this.#rid, p);
+ async read(p) {
+ if (p.length === 0) return 0;
+ this.#opPromise = core.read(this.#rid, p);
+ if (!this.#ref) {
+ core.unrefOpPromise(this.#opPromise);
+ }
+ const nread = await this.#opPromise;
+ return nread === 0 ? null : nread;
}
readSync(p) {
@@ -224,6 +236,20 @@ class Stdin {
isTerminal() {
return core.isTerminal(this.#rid);
}
+
+ [REF]() {
+ this.#ref = true;
+ if (this.#opPromise) {
+ core.refOpPromise(this.#opPromise);
+ }
+ }
+
+ [UNREF]() {
+ this.#ref = false;
+ if (this.#opPromise) {
+ core.unrefOpPromise(this.#opPromise);
+ }
+ }
}
class Stdout {
@@ -318,6 +344,7 @@ export {
readAll,
readAllSync,
readSync,
+ REF,
SeekMode,
Stderr,
stderr,
@@ -327,6 +354,7 @@ export {
Stdout,
stdout,
STDOUT_RID,
+ UNREF,
write,
writeSync,
};
diff --git a/ext/node/polyfills/tty.js b/ext/node/polyfills/tty.js
index c44f7e946..3a84a1f94 100644
--- a/ext/node/polyfills/tty.js
+++ b/ext/node/polyfills/tty.js
@@ -9,7 +9,10 @@ const {
} = core;
import { ERR_INVALID_FD } from "ext:deno_node/internal/errors.ts";
-import { LibuvStreamWrap } from "ext:deno_node/internal_binding/stream_wrap.ts";
+import {
+ kStreamBaseField,
+ LibuvStreamWrap,
+} from "ext:deno_node/internal_binding/stream_wrap.ts";
import { providerType } from "ext:deno_node/internal_binding/async_wrap.ts";
import { Socket } from "node:net";
import { setReadStream } from "ext:deno_node/_process/streams.mjs";
@@ -36,6 +39,14 @@ class TTY extends LibuvStreamWrap {
constructor(handle) {
super(providerType.TTYWRAP, handle);
}
+
+ ref() {
+ this[kStreamBaseField][io.REF]();
+ }
+
+ unref() {
+ this[kStreamBaseField][io.UNREF]();
+ }
}
export class ReadStream extends Socket {
diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs
index 6419f6ff0..eec10f64f 100644
--- a/tests/integration/run_tests.rs
+++ b/tests/integration/run_tests.rs
@@ -5359,3 +5359,32 @@ fn code_cache_npm_with_require_test() {
assert!(!output.stderr().contains("Updating V8 code cache"));
}
}
+
+#[test]
+fn node_process_stdin_unref_with_pty() {
+ TestContext::default()
+ .new_command()
+ .args_vec(["run", "--quiet", "run/node_process_stdin_unref_with_pty.js"])
+ .with_pty(|mut console| {
+ console.expect("START\r\n");
+ console.write_line("foo");
+ console.expect("foo\r\n");
+ console.write_line("bar");
+ console.expect("bar\r\n");
+ console.write_line("baz");
+ console.expect("baz\r\n");
+ });
+
+ TestContext::default()
+ .new_command()
+ .args_vec([
+ "run",
+ "--quiet",
+ "run/node_process_stdin_unref_with_pty.js",
+ "--unref",
+ ])
+ .with_pty(|mut console| {
+ // if process.stdin.unref is called, the program immediately ends by skipping reading from stdin.
+ console.expect("START\r\nEND\r\n");
+ });
+}
diff --git a/tests/testdata/run/node_process_stdin_unref_with_pty.js b/tests/testdata/run/node_process_stdin_unref_with_pty.js
new file mode 100644
index 000000000..e86304981
--- /dev/null
+++ b/tests/testdata/run/node_process_stdin_unref_with_pty.js
@@ -0,0 +1,14 @@
+import process from "node:process";
+import util from "node:util";
+
+console.log("START");
+globalThis.addEventListener("unload", () => console.log("END"));
+
+const args = util.parseArgs({ options: { unref: { type: "boolean" } } });
+
+// call stdin.unref if --unref is passed
+if (args.values.unref) {
+ process.stdin.unref();
+}
+
+process.stdin.pipe(process.stdout);