Node process hangs during `import('process')` if a sibling process reads from `stdin` and `stdin` is piped to a parent process
Personne n'a encore pris cette issue.
- Langage dominant
- JavaScript
- Étoiles
- 122k
- Forks
- 37.3k
- Merge moyen
- 4 j 2 h
- PR mergées (30 j)
- 283
Description
Version
All versions, tested on v12 through v23.6.0
Platform
Windows 10 and 11
What steps will reproduce the bug?
Launching a process tree like the following causes grandchild2 to hang (cannot attach Node inspector, no JS code executes) after calling import('process') (which never resolves, unless its sibling grandchild1 exits):
main.js - launches child with piped stdin
middle.js - launches children with inherited stdin
grandchild1.js - attaches a listener to stdin
(1s later) grandchild2.js - imports node:process and then hangs
Example code:
main.js
require('child_process').spawn('node', ['middle.js'], { stdio: ['pipe', 'inherit', 'inherit'] });
middle.js
const { spawn } = require('child_process');
spawn('node', ['grandchild1.js'], { stdio: 'inherit' });
// Note: this also works: spawn('cmd', ['/c', 'pause'], { stdio: 'inherit' });
setTimeout(function () {
spawn('node', ['grandchild2.js'], { stdio: 'inherit' });
}, 1000);
grandchild1.js
process.stdin.on('data', console.log);
grandchild2.js
import('process').then(function (mod) {
console.log('import(node:process) completed'); // this line is never executed
});
console.log(process.pid, `grandchild2 started - if you don't see "not stuck" below, it's stuck`);
setInterval(function () {
console.log(process.pid, 'not stuck');
}, 1000);
Example repo with the above code (plus a bit more logging): https://github.com/Jimbly/node-stdin-hang-bug-demo
Tested on (all fail):
- Node v12...v22
- Windows 10, 11
Works fine on Linux, MacOS, presumably all non-Windows platforms.
How often does it reproduce? Is there a required condition?
Always (on Windows).
Additional information
Hang does not occur if any of these happen:
grandchild1is launched beforegrandchild2middle'sstdinis notpiped tomaingrandchild1's orgrandchild2'sstdinis notinheritgrandchild1exits (causesgrandchild2to get unstuck)
Hang still occurs if:
grandchild1is anything that reads from stdio (e.g.cmd /c pause)
The hung process's (native) call stack is:
NtSetInformationFile()
SetNamedPipeHandleState(pipeHandle, PIPE_READMODE_BYTE | PIPE_WAIT)
node.exe!uv__set_pipe_handle(loop=0x00007ff71d27f130, handle=0x000001c0c83367f0, pipeHandle=0x0000000000000278, fd=-1, duplex_flags=49152)
at deps\uv\src\win\pipe.c(482)
node.exe!uv_pipe_open(pipe=0x000001c0c83367f0, file=0)
at deps\uv\src\win\pipe.c(2480)
node.exe!node::PipeWrap::Open(args={...})
at src\pipe_wrap.cc(209)
node.exe!Builtins_CallApiCallbackGeneric()
at out\Release\obj\v8_snapshot\embedded.S(4265)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_InterpreterPushArgsThenFastConstructFunction()
at out\Release\obj\v8_snapshot\embedded.S(4038)
node.exe!Builtins_ConstructHandler()
at out\Release\obj\v8_snapshot\embedded.S(56237)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_GetPropertyWithReceiver()
at out\Release\obj\v8_snapshot\embedded.S(24522)
node.exe!Builtins_ReflectGet()
at out\Release\obj\v8_snapshot\embedded.S(41510)
00007ff69938aaf7()
00007ff69938acf4()
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_JSEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3658)
node.exe!Builtins_JSEntry()
at out\Release\obj\v8_snapshot\embedded.S(3616)
[Inline Frame] node.exe!v8::internal::GeneratedCode<unsigned __int64,unsigned __int64,unsigned __int64,unsigned __int64,unsigned __int64,__int64,unsigned __int64 * *>::Call()
at deps\v8\src\execution\simulator.h(178)
node.exe!v8::internal::`anonymous namespace'::Invoke(isolate=0x000001c0c82c1000, params={...})
at deps\v8\src\execution\execution.cc(420)
node.exe!v8::internal::Execution::Call(isolate=0x000001c0c82c1000, callable, receiver, argc=0, argv=0x0000000000000000)
at deps\v8\src\execution\execution.cc(506)
node.exe!v8::Function::Call(context, recv={...}, argc=0, argv=0x0000000000000000)
at deps\v8\src\api\api.cc(5484)
node.exe!node::loader::ModuleWrap::SyntheticModuleEvaluationStepsCallback(context={...}, module)
at src\module_wrap.cc(948)
node.exe!v8::internal::SyntheticModule::Evaluate(isolate=0x000001c0c82c1000, module={...})
at deps\v8\src\objects\synthetic-module.cc(108)
node.exe!v8::internal::Module::Evaluate(isolate=0x000001c0c82c1000, module={...})
at deps\v8\src\objects\module.cc(284)
node.exe!v8::Module::Evaluate(context)
at deps\v8\src\api\api.cc(2461)
[Inline Frame] node.exe!node::loader::ModuleWrap::Evaluate::__l2::<lambda_1>::operator()()
at src\module_wrap.cc(562)
node.exe!node::loader::ModuleWrap::Evaluate(args={...})
at src\module_wrap.cc(578)
node.exe!Builtins_CallApiCallbackGeneric()
at out\Release\obj\v8_snapshot\embedded.S(4265)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_InterpreterEntryTrampoline()
at out\Release\obj\v8_snapshot\embedded.S(3946)
node.exe!Builtins_AsyncFunctionAwaitResolveClosure()
at out\Release\obj\v8_snapshot\embedded.S(11765)
node.exe!Builtins_PromiseFulfillReactionJob()
at out\Release\obj\v8_snapshot\embedded.S(40771)
node.exe!Builtins_RunMicrotasks()
at out\Release\obj\v8_snapshot\embedded.S(9703)
(truncated for size, see above repo if more stack is useful)
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Exécutez d’abord sous Windows la reproduction liée de node-stdin-hang-bug-demo. Suivez ensuite l’interaction à travers deps/uv/src/win/pipe.c, src/pipe_wrap.cc et src/module_wrap.cc, en utilisant la pile native signalée comme point de départ. C’est terminé lorsque le scénario stdin associé ne se bloque plus et que import('process') s’achève, tandis que la reproduction continue de fonctionner correctement.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- javascript, node.js
- Domaine
- operating-systems
- Type d'issue
- Bug
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Activité
- Calme
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 35/100