-
Notifications
You must be signed in to change notification settings - Fork 30.4k
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
process: process.stdin
getter sets O_NONBLOCK
flag in open file description
#42826
Comments
Detailsnode/lib/internal/bootstrap/switches/is_main_thread.js Lines 16 to 22 in 7319419
CASE
|
case 'TTY': { | |
const tty = require('tty'); | |
stdin = new tty.ReadStream(fd); | |
break; | |
} |
Line 47 in 7319419
function ReadStream(fd, options) { |
Line 54 in 7319419
const tty = new TTY(fd, ctx); |
Line 31 in 7319419
const { TTY, isTTY } = internalBinding('tty_wrap'); |
Lines 140 to 143 in 1c4df35
TTYWrap::TTYWrap(Environment* env, | |
Local<Object> object, | |
int fd, | |
int* init_err) |
Line 148 in 1c4df35
*init_err = uv_tty_init(env->event_loop(), &handle_, fd, 0); |
Line 123 in 1c4df35
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) { |
Line 204 in c61870c
uv__nonblock(fd, 1); |
CASE PIPE
node/lib/internal/bootstrap/switches/is_main_thread.js
Lines 192 to 194 in 7319419
case 'PIPE': | |
case 'TCP': { | |
const net = require('net'); |
node/lib/internal/bootstrap/switches/is_main_thread.js
Lines 208 to 209 in 7319419
stdin = new net.Socket({ | |
fd: fd, |
Line 291 in 7319419
function Socket(options) { |
Lines 356 to 358 in 7319419
this._handle = createHandle(fd, false); | |
err = this._handle.open(fd); |
Line 143 in 7319419
function createHandle(fd, is_server) { |
Lines 146 to 150 in 7319419
if (type === 'PIPE') { | |
return new Pipe( | |
is_server ? PipeConstants.SERVER : PipeConstants.SOCKET | |
); | |
} |
Lines 63 to 67 in 7319419
const { | |
Pipe, | |
PipeConnectWrap, | |
constants: PipeConstants | |
} = internalBinding('pipe_wrap'); |
Lines 154 to 157 in 7319419
PipeWrap::PipeWrap(Environment* env, | |
Local<Object> object, | |
ProviderType provider, | |
bool ipc) |
Line 208 in 7319419
void PipeWrap::Open(const FunctionCallbackInfo<Value>& args) { |
Line 217 in 7319419
int err = uv_pipe_open(&wrap->handle_, fd); |
Line 137 in 7319419
int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { |
Line 153 in 7319419
err = uv__nonblock(fd, 1); |
What you're describing is expected behavior; node has basically always behaved that way, at least since the libuv switchover in the v0.5 days, and probably before that. That said, instead of putting the file descriptor into non-blocking mode, libuv could use sendmsg() and recvmsg() with the MSG_DONTWAIT flag - assuming all supported platforms support it. |
Updated #42826 (comment), happens also on pipes. |
Turns out sendmsg/recvmsg don't work on enough file descriptor types for my suggestion to be feasible. The current situation is probably as good as it gets. |
thinking aloud: non-blocking behaviour of the standard streams and pipes make sense; but should the |
No, because that effectively turns the operation into a busy loop.
Sure, but that's like documenting water is wet. |
thanks @bnoordhuis
https://nodejs.org/api/fs.html#synchronous-example already warns about implications of using sync variant as blocking the event loop for a period of time till the operation complete. But of course, agree that busy loop is not a good idea.
This one I am finding it difficult to relate. At the system programming level, an I/O call on a non-blocking , non-ready descriptor throwing may be well understood, but at the Node.js API level where things are abstracted, this behaviour cannot be implied, and is not specified anywhere in the docs? |
OP's example is The surprise seems to be that stdio fds in node are sometimes blocking, sometimes non-blocking. I agree that's something worth documenting. |
I'm going to close this out. If anyone still wants to send a documentation pull request, please do. |
Version
v18.0.0
Platform
Subsystem
process
What steps will reproduce the bug?
TTY
$ node -e 'process.stdin; require("fs").readSync(0, Buffer.alloc(1))'
PIPE
How often does it reproduce? Is there a required condition?
Consistently reproducible by making use of the
process.stdin
getter.What is the expected behavior?
Read a single byte from the standard input and exit gracefully.1
What do you see instead?
Additional information
The mere fact of accessing
process.stdin
causes libuv to set theO_NONBLOCK
flag in the open file description, altering the behavior offs.readSync(0, buffer)
and throwingEAGAIN
if there isn't enough data to be read.Awareness
It looks like the behavior of that getter doing “things” is well–known internally, and several tests make use of this “feature”
node/test/parallel/test-child-process-stdin-ipc.js
Lines 29 to 30 in bde889b
node/test/parallel/test-stdin-from-file-spawn.js
Lines 27 to 28 in bde889b
node/test/parallel/test-stdin-hang.js
Lines 25 to 30 in bde889b
Related
Footnotes
Id est: the same behavior as running
node -e 'require("fs").readSync(0, Buffer.alloc(1))'
without calling theprocess.stdin
getter first. ↩The text was updated successfully, but these errors were encountered: