Node process hangs during `import('process')` if a sibling process reads from `stdin` and `stdin` is piped to a parent process
Nadie ha tomado este issue todavía.
- Lenguaje dominante
- JavaScript
- Estrellas
- 122k
- Forks
- 37.3k
- Merge medio
- 4 d 2 h
- PR fusionados (30 d)
- 283
Descripción
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)
Guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Línea de trabajo
Ejecuta primero en Windows la reproducción enlazada de node-stdin-hang-bug-demo. Después, sigue la interacción a través de deps/uv/src/win/pipe.c, src/pipe_wrap.cc y src/module_wrap.cc, usando la pila nativa indicada como punto de partida. Se considera terminado cuando el escenario hermano de stdin ya no se bloquea y import('process') se completa, mientras la reproducción sigue comportándose correctamente.
Escrito por el modelo de indexación a partir del texto del issue.
Evaluación
- Stack tecnológico
- javascript, node.js
- Área
- operating-systems
- Tipo de issue
- Error
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Estado de actividad
- Tranquilo
- Claridad
- Bien especificado
- Aptitud para principiantes
- 35/100