Windows: ConoutConnection worker thread prevents Node.js from exiting after kill()
还没有人认领这个 Issue。
评估
- 难度
- 3/5
- 预计耗时
- 1-2 天
- 新手友好度
- 70/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 冷清
- 技术栈
- node.js, typescript
- 领域
- cli
调研方向
使用提供的 Node.js 脚本复现 Windows ConPTY 挂起问题,然后检查所引用行中的 src/windowsConoutConnection.ts、src/worker/conoutSocketWorker.ts 和 src/windowsPtyAgent.ts。检查 kill() 之后涉及的 worker、sockets 和 cleanup 超时。完成的标准是:进程在 exit 事件之后退出,而不需要 process.exit();同时,当其他工作使 event loop 保持活动时,cleanup 仍能完成。
由索引模型根据 Issue 内容生成。
描述
Environment
- OS: Windows (ConPTY path)
- node-pty version: 1.2.0-beta.10
- Node.js: v22.22.0
Description
After calling kill() on a Windows PTY and awaiting its exit event, Node.js cannot exit because active handles remain on the event loop. This forces consumers to call process.exit() as a workaround.
The root cause is that the ConoutConnection worker thread, its internal sockets, and several cleanup timeouts are never unref()'d. There is no .unref() call anywhere in node-pty's source.
Reproduction
const pty = require('node-pty');
const term = pty.spawn('cmd.exe', [], { cols: 80, rows: 24 });
term.onExit(() => {
console.log('exited');
// Node.js should exit here, but it hangs indefinitely
});
setTimeout(() => term.kill(), 500);
Expected: process exits after "exited" is logged.
Actual: process hangs indefinitely.
Root cause
There are three categories of handles keeping the event loop alive after kill():
1. Worker thread (primary)
src/windowsConoutConnection.ts:47 — the Worker is created but never unref()'d:
this._worker = new Worker(join(scriptPath, 'worker/conoutSocketWorker.js'), { workerData });
The worker runs a net.Socket + net.createServer (src/worker/conoutSocketWorker.ts:11-17) that are also never unref()'d. Both the worker thread and its internal sockets keep the event loop alive.
2. Drain timeout
When dispose() is called, it schedules a 1-second timeout before terminating the worker (src/windowsConoutConnection.ts:76):
this._drainTimeout = setTimeout(() => this._destroySocket(), FLUSH_DATA_INTERVAL);
This timeout is not unref()'d, keeping the event loop alive for the duration.
A similar un-unref()'d timeout exists in WindowsPtyAgent._flushDataAndCleanUp() (src/windowsPtyAgent.ts:179):
this._closeTimeout = setTimeout(() => this._cleanUpProcess(), FLUSH_DATA_INTERVAL);
3. I/O sockets
_outSocket and _inSocket in WindowsPtyAgent (src/windowsPtyAgent.ts:78-94) are never unref()'d.
Suggested fix
Call unref() on handles that shouldn't prevent process exit:
// windowsConoutConnection.ts — constructor
this._worker = new Worker(join(scriptPath, 'worker/conoutSocketWorker.js'), { workerData });
this._worker.unref();
// windowsConoutConnection.ts — _drainDataAndClose
this._drainTimeout = setTimeout(() => this._destroySocket(), FLUSH_DATA_INTERVAL);
this._drainTimeout.unref();
// windowsPtyAgent.ts — constructor (sockets)
this._outSocket.unref();
this._inSocket.unref();
// windowsPtyAgent.ts — _flushDataAndCleanUp
this._closeTimeout = setTimeout(() => this._cleanUpProcess(), FLUSH_DATA_INTERVAL);
this._closeTimeout.unref();
This ensures cleanup completes if other work keeps the event loop alive, but doesn't prevent the process from exiting when everything else is done.
- 主要语言
- TypeScript
- 星标
- 2k
- 派生
- 337
- 平均合并
- 21 小时 58 分钟
- 30 天内合并 PR
- 3
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
microsoft/node-pty 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 78/100
-
难度 2/5 1-3 小时 新手友好度 76/100
-
难度 2/5 1-3 小时 新手友好度 82/100
-
难度 2/5 1-3 小时 新手友好度 76/100
-
难度 2/5 1-3 小时 新手友好度 76/100
查看 microsoft/node-pty 的全部 Issue
相似的 Issue
-
bug(cli): hapi doctor inline-media prints a fabricated B:\ helper-script path in packaged installs 未关闭
难度 2/5 1-3 小时 新手友好度 70/100
-
Crush 未关闭
难度 1/5 1 小时以内 新手友好度 85/100
catppuccin/catppuccin#3125 ·
-
难度 1/5 1 小时以内 新手友好度 90/100
ElementsProject/cln-application#167 · 1 条评论 · 1 个 reaction ·
-
难度 2/5 1-3 小时 新手友好度 75/100
Quantco/pnpm-licenses#17 ·
-
难度 2/5 1-3 小时 新手友好度 75/100