* fix: Remove double-kill bug while keeping PowerShell (#777) Problems fixed: - Removed double TASKKILL execution when SendInterrupt is enabled - Simplified kill logic to single TASKKILL command - Added console window hiding for TASKKILL and PowerShell - Improved PowerShell with -NoProfile and -NonInteractive flags Key difference from reverted PR #855: - Kept PowerShell instead of cmd.exe to avoid sound issues (#707) - Still fixes the double-kill bug that caused orphaned processes - Added window hiding for cleaner UX on Windows - Better PowerShell performance with optimized flags The previous code would run TASKKILL twice when SendInterrupt was enabled, causing processes to not be properly terminated and leading to port conflicts and orphaned processes. This fix addresses #777 without introducing #707 sound issues. Testing on Linux shows clean process transitions with no port conflicts. Fixes #777 Avoids #707 * fix: clarify Windows send_interrupt handling Document that Windows ignores send_interrupt and use CREATE_NO_WINDOW for clearer process spawning. * Add env file config and warn on dangerous roots * Add default env file list --------- Co-authored-by: xiantang <zhujingdi1998@gmail.com>
This commit is contained in:
+1
-1
@@ -54,7 +54,7 @@ poll_interval = 500 # ms
|
||||
delay = 0 # ms
|
||||
# Stop running old binary when build errors occur.
|
||||
stop_on_error = true
|
||||
# Send Interrupt signal before killing process (windows does not support this feature)
|
||||
# Send Interrupt signal before killing process (ignored on Windows; uses TASKKILL)
|
||||
send_interrupt = false
|
||||
# Delay after sending Interrupt signal
|
||||
kill_delay = 500 # nanosecond
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ type cfgBuild struct {
|
||||
PollInterval int `toml:"poll_interval" usage:"Poll interval (defaults to the minimum interval of 500ms)"`
|
||||
Delay int `toml:"delay" usage:"It's not necessary to trigger build each time file changes if it's too frequent"`
|
||||
StopOnError bool `toml:"stop_on_error" usage:"Stop running old binary when build errors occur"`
|
||||
SendInterrupt bool `toml:"send_interrupt" usage:"Send Interrupt signal before killing process (windows does not support this feature)"`
|
||||
SendInterrupt bool `toml:"send_interrupt" usage:"Send Interrupt signal before killing process (ignored on Windows; uses TASKKILL)"`
|
||||
KillDelay time.Duration `toml:"kill_delay" usage:"Delay after sending Interrupt signal"`
|
||||
Rerun bool `toml:"rerun" usage:"Rerun binary or not"`
|
||||
RerunDelay int `toml:"rerun_delay" usage:"Delay after each execution"`
|
||||
|
||||
+34
-10
@@ -1,3 +1,5 @@
|
||||
//go:build windows
|
||||
|
||||
package runner
|
||||
|
||||
import (
|
||||
@@ -6,23 +8,35 @@ import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (e *Engine) killCmd(cmd *exec.Cmd) (pid int, err error) {
|
||||
pid = cmd.Process.Pid
|
||||
// https://stackoverflow.com/a/44551450
|
||||
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(pid))
|
||||
|
||||
// On Windows, SIGINT is not supported for process trees
|
||||
// Use TASKKILL to forcefully terminate the process hierarchy
|
||||
if e.config.Build.SendInterrupt {
|
||||
if err = kill.Run(); err != nil {
|
||||
return
|
||||
}
|
||||
time.Sleep(e.config.killDelay())
|
||||
e.mainLog("send_interrupt is not supported on Windows, using TASKKILL instead")
|
||||
}
|
||||
err = kill.Run()
|
||||
// Wait releases any resources associated with the Process.
|
||||
|
||||
// Single TASKKILL execution to avoid double-kill bug
|
||||
e.mainDebug("sending TASKKILL to process tree")
|
||||
killCmd := exec.Command("TASKKILL", "/F", "/T", "/PID", strconv.Itoa(pid))
|
||||
|
||||
// Hide the taskkill console window
|
||||
killCmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
CreationFlags: windows.CREATE_NO_WINDOW,
|
||||
}
|
||||
|
||||
err = killCmd.Run()
|
||||
|
||||
// Wait for process to terminate and release resources
|
||||
_, _ = cmd.Process.Wait()
|
||||
|
||||
return pid, err
|
||||
}
|
||||
|
||||
@@ -32,7 +46,17 @@ func (e *Engine) startCmd(cmd string) (*exec.Cmd, io.ReadCloser, io.ReadCloser,
|
||||
if !strings.Contains(cmd, ".exe") {
|
||||
e.runnerLog("CMD will not recognize non .exe file for execution, path: %s", cmd)
|
||||
}
|
||||
c := exec.Command("powershell", cmd)
|
||||
|
||||
// Keep PowerShell to avoid cmd.exe sound issues (#707)
|
||||
// Use -NoProfile and -NonInteractive for better performance
|
||||
c := exec.Command("powershell", "-NoProfile", "-NonInteractive", "-Command", cmd)
|
||||
|
||||
// Hide the PowerShell console window
|
||||
c.SysProcAttr = &syscall.SysProcAttr{
|
||||
HideWindow: true,
|
||||
CreationFlags: windows.CREATE_NO_WINDOW,
|
||||
}
|
||||
|
||||
stderr, err := c.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
||||
Reference in New Issue
Block a user