Skip to content

fix: unify child-process stop semantics with await-exit and SIGKILL escalation #503

Description

@LeXwDeX

Why

深度审计确认(孤儿进程向量,无 zombie——Bun/Node 自动收割,但孤儿成立):

  1. packages/opencode/src/util/process.ts:149-155Process.stop POSIX 路径 proc.kill()(SIGTERM)后立即返回——不 await proc.exited、无 SIGKILL 升级。调用点 lsp/client.ts:640lsp/lsp.ts:198-246 的实例 finalizer 因此可能「正常完成但进程还活着」;忽略 SIGTERM 的 language server 永久孤儿(Windows 路径有 taskkill /T /F 对照,POSIX 缺失等价语义)
  2. packages/opencode/src/mcp/index.ts:533-558:MCP state finalizer 对 descendants(pid):416-438)只发一轮 SIGTERM,无升级、不等待;closeClient/storeClient:564-589)与 create 失败回滚(:400-403)只调 client.close(),完全跳过孙进程清理;pgrep -P 快照遍历期间新派生的后代逃逸;Windows 上 descendants 恒为 []
  3. 放大器:packages/opencode/src/index.ts:141 CLI finally 无条件 process.exit() 放弃异步 finalizer——在注释声明的取舍(docker MCP 不响应 SIGTERM)之上,前置的异步 kill+await 若未完成,子进程直接孤儿化

Scope

  • packages/opencode/src/util/process.ts(注意文件头注释:packages/sdk/js/src/process.ts 有同步副本,必须一起改)
  • packages/opencode/src/mcp/index.ts
  • packages/opencode/src/index.ts 退出路径(给异步清理一个有界窗口,而非无限等待)

Approach

  • Process.stop:SIGTERM → 有界等待 exit(如 3-5s)→ SIGKILL 升级 → await exit(与 Windows taskkill /T /F 语义对齐);SDK 副本同步
  • MCP:descendants 清理提取为可复用例程,kill 走升级语义并等待;closeClient/storeClient/create 回滚路径同样覆盖孙进程
  • index.ts:finally 中对实例清理给有界窗口(超时后仍 process.exit(),保持既有取舍但缩小竞态面)

Acceptance

  • 单测(注入假 ChildProcess / 测试专用 server):SIGTERM-忽略的子进程在 Process.stop 升级窗口内被 SIGKILL 终止且 exit 被 await
  • MCP 动态 disconnect/reconnect 路径孙进程被清理的回归测试
  • 既有 process/mcp/lsp 测试全绿;SDK 副本 diff 一致

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions