New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
difference in broken pipe behaviour for console.log()
& process.stdout.write()
#1632
Comments
Discrepancies in behaviour can be observed without any $ cat exec.js
const child = require("child_process").exec(process.argv.slice(2).join(" "));
child.stdin.on("close", () => {
console.log("stdin CLOSED");
}).on("end", () => {
console.log("stdin ENDED");
}).on("error", (err) => {
console.error("stdin", err);
}).end("foo\nbar\n");
child.stdout.on("data", data => {
console.log(data);
}).on("close", () => {
console.log("stdout CLOSED");
}).on("end", () => {
console.log("stdout ENDED");
}).on("error", (err) => {
console.error("stdout", err);
}); $ cat grep.js
const filter = process.argv[2];
let result = "";
process.stdin.setEncoding("utf8").on("data", data => {
result += data;
}).on("end", () => {
result.split(/\n/).filter(line => line.indexOf(filter) >= 0).forEach(line => {
process.stdout.write(line + "\n");
});
}); Added native binary $ node exec.js grep o
stdin CLOSED
foo
stdout ENDED
stdout CLOSED
$ node exec.js node grep.js o
stdin CLOSED
foo
stdout ENDED
stdout CLOSED
$ node exec.js bun grep.js o
stdin CLOSED
stdout ENDED
stdout CLOSED Observe above that output from $ bun exec.js grep o
stdin CLOSED
$ bun exec.js node grep.js o
stdin CLOSED
foo
stdout ENDED
stdout CLOSED
$ bun exec.js bun grep.js o
stdin CLOSED
foo
stdout ENDED
stdout CLOSED While |
This reads like an event loop queue order bug
As a quick test, if you reassign |
--- a/src/bun.js/streams.exports.js
+++ b/src/bun.js/streams.exports.js
@@ -105,7 +105,7 @@ var __copyProps = (to, from, except, desc) => {
return to;
};
-var runOnNextTick = process.nextTick;
+var runOnNextTick = (cb, ...args) => setTimeout(cb, 0, undefined, ...args);
function isReadableStream(value) {
return ( $ export BUN_OVERRIDE_MODULE_PATH=/path/to/bun/repo No luck on the child ( $ node exec.js bun grep.js o
stdin CLOSED
stdout ENDED
stdout CLOSED RUD with parent ( $ bun exec.js grep o
4326 | }
4327 | }
4328 | }
4329 | function finish(stream, state) {
4330 |
4331 | state.pendingcb--;
^
TypeError: undefined is not an object (evaluating 'state.pendingcb')
at finish (node:stream:4331:6)
foo
2154 | emitErrorNT(self, err);
2155 | emitCloseNT(self);
2156 | }
2157 | function emitCloseNT(self) {
2158 |
2159 | const r = self._readableState;
^
TypeError: undefined is not an object (evaluating 'self._readableState')
at emitCloseNT (node:stream:2159:16)
3705 | state.length,
3706 | stream.__id,
3707 | );
3708 | if (
3709 |
3710 | !state.errored &&
^
TypeError: undefined is not an object (evaluating 'state.errored')
at endReadableNT (node:stream:3710:9)
3705 | state.length,
3706 | stream.__id,
3707 | );
3708 | if (
3709 |
3710 | !state.errored &&
^
TypeError: undefined is not an object (evaluating 'state.errored')
at endReadableNT (node:stream:3710:9) |
Also tried: var runOnNextTick = (cb, ...args) => setTimeout(cb, 0, ...args); var runOnNextTick = setImmediate; without any (further) changes in behaviour. |
what about setTimeout(1) |
Are you sure about that? 🤔 https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
https://nodejs.org/dist/latest-v18.x/docs/api/timers.html#settimeoutcallback-delay-args
$ echo 'setTimeout(console.log, 0, "arg1", "arg2")' | node
arg1 arg2 |
Tried $ bun exec.js grep o
foo
4326 | }
4327 | }
4328 | }
4329 | function finish(stream, state) {
4330 |
4331 | state.pendingcb--;
^
TypeError: undefined is not an object (evaluating 'state.pendingcb')
at finish (node:stream:4331:6)
2154 | emitErrorNT(self, err);
2155 | emitCloseNT(self);
2156 | }
2157 | function emitCloseNT(self) {
2158 |
2159 | const r = self._readableState;
^
TypeError: undefined is not an object (evaluating 'self._readableState')
at emitCloseNT (node:stream:2159:16)
3705 | state.length,
3706 | stream.__id,
3707 | );
3708 | if (
3709 |
3710 | !state.errored &&
^
TypeError: undefined is not an object (evaluating 'state.errored')
at endReadableNT (node:stream:3710:9)
3705 | state.length,
3706 | stream.__id,
3707 | );
3708 | if (
3709 |
3710 | !state.errored &&
^
TypeError: undefined is not an object (evaluating 'state.errored')
at endReadableNT (node:stream:3710:9) Notice that |
No, I was wrong. I didn't see the comment above it before sending comment
i wonder if this means we are missing a |
That's odd then, since all the var runOnNextTick = (cb, ...args) => {
console.log(cb.name, args[0] != null, args[1] != null);
setTimeout(cb, 0, ...args);
}; $ bun exec.js grep o
finish true true
emitCloseNT true false
4329 | }
4330 | }
4331 | }
4332 | function finish(stream, state) {
4333 |
4334 | state.pendingcb--;
^
TypeError: undefined is not an object (evaluating 'state.pendingcb')
at finish (node:stream:4334:6)
foo
endReadableNT true true
endReadableNT true true
2157 | emitErrorNT(self, err);
2158 | emitCloseNT(self);
2159 | }
2160 | function emitCloseNT(self) {
2161 |
2162 | const r = self._readableState;
^
TypeError: undefined is not an object (evaluating 'self._readableState')
at emitCloseNT (node:stream:2162:16)
3708 | state.length,
3709 | stream.__id,
3710 | );
3711 | if (
3712 |
3713 | !state.errored &&
^
TypeError: undefined is not an object (evaluating 'state.errored')
at endReadableNT (node:stream:3713:9)
3708 | state.length,
3709 | stream.__id,
3710 | );
3711 | if (
3712 |
3713 | !state.errored &&
^
TypeError: undefined is not an object (evaluating 'state.errored')
at endReadableNT (node:stream:3713:9) |
after working around #1633: --- a/src/bun.js/streams.exports.js
+++ b/src/bun.js/streams.exports.js
@@ -105,7 +105,11 @@ var __copyProps = (to, from, except, desc) => {
return to;
};
-var runOnNextTick = process.nextTick;
+var runOnNextTick = (cb, ...args) => {
+ setTimeout(() => {
+ cb(...args);
+ }, 0);
+};
function isReadableStream(value) {
return ( No changes to the child ( $ node exec.js grep o
stdin CLOSED
foo
stdout ENDED
stdout CLOSED
$ node exec.js node grep.js o
stdin CLOSED
foo
stdout ENDED
stdout CLOSED
$ node exec.js bun grep.js o
stdin CLOSED
stdout ENDED
stdout CLOSED
However, the behaviour in the parent ( $ bun exec.js grep o
foo
stdin CLOSED
stdout ENDED
stdout CLOSED
$ bun exec.js node grep.js o
foo
stdin CLOSED
stdout ENDED
stdout CLOSED
$ bun exec.js bun grep.js o
foo
stdin CLOSED
stdout ENDED
stdout CLOSED |
Changing the timeout from $ bun exec.js node grep.js o
foo
stdout ENDED
stdin CLOSED
stdout CLOSED |
Using Bun's API for the child, with more logging: $ cat grep-bun.js
const filter = process.argv[2];
try {
for await (const line of console) {
if (line.indexOf(filter) >= 0) console.log(line);
}
} catch (err) {
console.log("grep ERROR");
console.log(err);
} Reason for the missing output when parent is $ node exec.js bun grep-bun.js o
stdin CLOSED
grep ERROR
1 | const filter = process.argv[2];
2 | try {
3 | for await (const line of console) {
^
EINVAL: Invalid argument
syscall: "fstat"
errno: -22
at /home/alexlamsl/grep-bun.js:3:19
stdout ENDED
stdout CLOSED Whereas if $ bun exec.js bun grep-bun.js o
stdin CLOSED
foo
stdout ENDED
stdout CLOSED |
What version of Bun is running?
--canary
What platform is your computer?
Linux 5.15.0-56-generic x86_64 x86_64
What steps can reproduce the bug?
What is the expected behavior?
What do you see instead?
Additional information
Discovered while investigating
bun wiptest
failures in:bun/test/bun.js/child_process-node.test.js
Lines 200 to 258 in 79abd24
The text was updated successfully, but these errors were encountered: