You've already forked agentic-coding-workflow
启动项目逻辑
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
//go:build !windows
|
||||
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestUnixGroup_KillsChildProcess 验证整树清理:父 shell 派生一个后台 sleep
|
||||
// 子进程并打印其 PID,TerminateGroup 后该子进程(孙级)也应被 kill(-pgid) 回收,
|
||||
// 证明终止覆盖整个进程组而非仅组长。
|
||||
func TestUnixGroup_KillsChildProcess(t *testing.T) {
|
||||
g := NewGroup()
|
||||
// `sleep 30 &` 后台子进程;`echo $!` 打印其 PID;`wait` 让 shell 阻塞存活。
|
||||
cmd := exec.Command("sh", "-c", "sleep 30 & echo $!; wait")
|
||||
g.Prepare(cmd)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("stdout pipe: %v", err)
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("start: %v", err)
|
||||
}
|
||||
if err := g.Adopt(cmd); err != nil {
|
||||
t.Fatalf("adopt: %v", err)
|
||||
}
|
||||
defer g.Close()
|
||||
|
||||
var childPID int
|
||||
if _, err := fmt.Fscan(stdout, &childPID); err != nil {
|
||||
t.Fatalf("read child pid: %v", err)
|
||||
}
|
||||
if childPID <= 0 {
|
||||
t.Fatalf("invalid child pid: %d", childPID)
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
_ = cmd.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
g.TerminateGroup(done, 2*time.Second)
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatal("parent not terminated")
|
||||
}
|
||||
|
||||
// 给内核一点时间回收子进程,然后用 kill(pid, 0) 探测:返回错误(ESRCH)=已死。
|
||||
deadline := time.Now().Add(5 * time.Second)
|
||||
for {
|
||||
if err := syscall.Kill(childPID, 0); err != nil {
|
||||
return // 子进程已死,符合预期。
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatalf("child process %d still alive after group terminate", childPID)
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user