You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
stdio regression: process.stdin getter drops pending stdin data #36251
In v15, if process.stdin is referenced, and is not a TTY or Pipe, then its data will be not be seen when passed to a child process. It's almost like it's already consumed or something.
$ node -v
v15.3.0
$ cat shell.js
const { spawn } = require('child_process')
// just reference the object, don't do anything with it
if (process.env.LOAD_STDIN === '1')
process.stdin
const child = spawn(process.env.SHELL, [], { stdio: 'inherit' })
child.on('close', (code, signal) => console.error({ code, signal }))
$ cat cmd.sh
#!/bin/bash
echo "hello from node's child shell"
$ node shell.js < cmd.sh
hello from node's child shell
{ code: 0, signal: null }
## This is the error:
$ LOAD_STDIN=1 node shell.js < cmd.sh
{ code: 0, signal: null }
## but this works, which is interesting?
$ cat cmd.sh | LOAD_STDIN=1 node shell.js
hello from node's child shell
{ code: 0, signal: null }
$ nave use 14
$ node -v
v14.15.1
$ node shell.js < cmd.sh
hello from node's child shell
{ code: 0, signal: null }
$ LOAD_STDIN=1 node shell.js < cmd.sh
hello from node's child shell
{ code: 0, signal: null }
$ cat cmd.sh | LOAD_STDIN=1 node shell.js
hello from node's child shell
{ code: 0, signal: null }
How often does it reproduce? Is there a required condition?
100% of the time on node 15. Not in any prior version tested (14, 12, and 10).
What is the expected behavior?
Stdin data should not be consumed when process.stdin is merely referenced. This causes problems when a script checks process.stdin.isTTY to print a helpful message prior to opening a shell in a child process.
What do you see instead?
Stdin data is not passed to child process if stdin is referenced, and not a Pipe or TTY.
Additional information
This kind of is a pain for npm exec. It means you can't do something like npx some-interactive-thing < list-of-commands.
@gireeshpunathil Yes there is. The ReadingStream starts reading immediately as well as the clone/exec path. This is the race condition. If libuv was able to read from the fd before the clone/exec, that the data is lost.
On Linux, at least on my machine, strace allows you to observe both situations. @joyeecheung maybe create the ReadStream in stopped state?
The process 661087 is exec'ing into bash but does ot get any data, due to the previous read by 661088 - but what it is? (it is cloned from 661080 which is the parent process)
@gireeshpunathil@isaacs I solved it by implementing a manualStart for ReadStream then using it in the process.stdin getter, just a sec to clean my debug printfs and if the unit tests pass, I will submit the PR
Activity
mmomtchev commentedon Nov 26, 2020
@isaacs, there is a relevent discussion here #35997
The creation of the implicit stdio
ReadStreamis in theprocess.stdiogettergireeshpunathil commentedon Nov 26, 2020
able to recreate with the said version, but not consistently.
so I believe there is a race condition happening here.
mmomtchev commentedon Nov 26, 2020
@gireeshpunathil Yes there is. The
ReadingStreamstarts reading immediately as well as theclone/execpath. This is the race condition. If libuv was able to read from the fd before theclone/exec, that the data is lost.On Linux, at least on my machine,
straceallows you to observe both situations.@joyeecheung maybe create the
ReadStreamin stopped state?gireeshpunathil commentedon Nov 26, 2020
@mmomtchev - thanks, I too got a linux box which recreates pretty easily.
passing case:
the process
661072is exec'ing intobashand reads from 0.failing case:
The process
661087is exec'ing intobashbut does ot get any data, due to the previous read by661088- but what it is? (it is cloned from661080which is the parent process)mmomtchev commentedon Nov 26, 2020
@gireeshpunathil @isaacs I solved it by implementing a
manualStartforReadStreamthen using it in theprocess.stdingetter, just a sec to clean my debug printfs and if the unit tests pass, I will submit the PRmmomtchev commentedon Nov 26, 2020
@gireeshpunathil All other stdio
ReadStreamstart in paused mode - except file - which didn't have amanualStartoptionmmomtchev commentedon Nov 26, 2020
@isaacs @gireeshpunathil This will be a platform-dependent unit test, exclusive to
darwinandlinuxunless you have an idea?stdio: implement manual start for ReadStream
mmomtchev commentedon Nov 26, 2020
The unit test is awful, I admit, if you have any ideas, fell free to comment
stdio: implement manual start for ReadStream
stream: lazy read ReadStream
stream: lazy read ReadStream