mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-03 15:57:24 +00:00
Every wrapped tool invocation (cl/link/msbuild/etc via wine, plus the native cmd/findstr shims) now starts its child in its own process group and forwards SIGINT/SIGTERM to that group, escalating to SIGKILL after a 5s grace period if it doesn't exit. Previously, interactive Ctrl-C happened to work by accident (the child inherited the terminal's foreground process group and got the signal directly), but anything that signals vintner by PID alone - a CI job's timeout, a supervisor's `kill <pid>` - never reached the wine/wineserver tree underneath it, which got reparented to init and kept running: wasted CPU, held file locks, stray FIFOs/temp files. Verified two ways: a unit test (signals_test.go) that starts a detached `sleep 30`, signals the test process itself, and checks the child actually dies; and a real end-to-end run - killed an in-flight `msbuild` driver build by PID mid-compile and confirmed no orphaned msbuild/cl/link/vintner process was left behind (wineserver and its persistent service processes are expected to survive, by design - see pipeDrainGrace's doc comment).
44 lines
1.2 KiB
Go
44 lines
1.2 KiB
Go
package wrapper
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestForwardSignalsKillsChild verifies the actual mechanism that keeps a
|
|
// wine subprocess from being orphaned: a SIGTERM delivered to the current
|
|
// process (mimicking `kill <vintner-pid>`, not an interactive Ctrl-C) must
|
|
// reach a child started with setNewProcessGroup, even though it's no longer
|
|
// in the same process group.
|
|
func TestForwardSignalsKillsChild(t *testing.T) {
|
|
cmd := exec.Command("sleep", "30")
|
|
setNewProcessGroup(cmd)
|
|
if err := cmd.Start(); err != nil {
|
|
t.Fatalf("starting sleep: %v", err)
|
|
}
|
|
stop := forwardSignals(cmd.Process)
|
|
defer stop()
|
|
|
|
// signal.Notify (inside forwardSignals) intercepts this rather than
|
|
// letting it terminate the test binary itself.
|
|
if err := syscall.Kill(os.Getpid(), syscall.SIGTERM); err != nil {
|
|
t.Fatalf("signaling self: %v", err)
|
|
}
|
|
|
|
done := make(chan error, 1)
|
|
go func() { done <- cmd.Wait() }()
|
|
|
|
select {
|
|
case err := <-done:
|
|
if err == nil {
|
|
t.Fatal("expected the child to be killed by the forwarded signal, but it exited successfully")
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
cmd.Process.Kill()
|
|
t.Fatal("child was still running 3s after the signal should have been forwarded")
|
|
}
|
|
}
|