mirror of
https://github.com/Cheviiot/Vintner.git
synced 2026-08-04 00:07:24 +00:00
Stop orphaning wine subprocesses when vintner is killed by PID
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).
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user