This commit is contained in:
2026-06-20 19:16:44 +08:00
parent db3d030169
commit dbb87823e8
26 changed files with 676 additions and 115 deletions
+5 -1
View File
@@ -41,8 +41,12 @@ func (g *unixGroup) TerminateGroup(done <-chan struct{}, grace time.Duration) {
_ = syscall.Kill(-g.pgid, syscall.SIGTERM)
select {
case <-done:
return
// 组长已被回收(Cmd.Wait 返回),但其子孙(如 trap SIGTERM 的
// vite/esbuild/node)可能仍存活于同一进程组。无条件向整组补发一次
// SIGKILL 以确保不留下任何孤儿;若组已全灭,内核返回 ESRCH,无害。
_ = syscall.Kill(-g.pgid, syscall.SIGKILL)
case <-time.After(grace):
// 组长自身超过 grace 仍未退出:直接对整组 SIGKILL。
_ = syscall.Kill(-g.pgid, syscall.SIGKILL)
}
}
+12 -5
View File
@@ -5,6 +5,7 @@ package proc
import (
"errors"
"os/exec"
"sync"
"time"
"unsafe"
@@ -15,7 +16,8 @@ import (
// KILL_ON_JOB_CLOSE 的 Job,此后该进程派生的所有子孙自动归入同一 Job,
// 关闭 Job 句柄即整树终止。优于 Process.Kill(只杀单 PID,漏 node 等子进程)。
type windowsGroup struct {
job windows.Handle
job windows.Handle
closeOnce sync.Once
}
func newGroup() Group { return &windowsGroup{} }
@@ -65,9 +67,14 @@ func (g *windowsGroup) TerminateGroup(_ <-chan struct{}, _ time.Duration) {
g.Close()
}
// Close 幂等且并发安全:TerminateGroup 与 monitor goroutine 可能并发调用,
// 用 sync.Once 保证 CloseHandle 至多执行一次,避免重复关闭句柄(可能误关被复用
// 的句柄)及对 g.job 的数据竞争。不改变 KILL_ON_JOB_CLOSE 语义:句柄关闭即整树终止。
func (g *windowsGroup) Close() {
if g.job != 0 {
_ = windows.CloseHandle(g.job)
g.job = 0
}
g.closeOnce.Do(func() {
if g.job != 0 {
_ = windows.CloseHandle(g.job)
g.job = 0
}
})
}