From aa4a5ac6aafc979b8498265812a044493b0714a9 Mon Sep 17 00:00:00 2001 From: Seth Brasile Date: Mon, 29 Apr 2024 20:11:15 -0500 Subject: [PATCH 1/8] hold cleanup until procKilledCh signal --- runner/engine.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runner/engine.go b/runner/engine.go index ee81bf30..a02e2c6e 100644 --- a/runner/engine.go +++ b/runner/engine.go @@ -30,6 +30,7 @@ type Engine struct { buildRunStopCh chan bool binStopCh chan bool exitCh chan bool + procKilledCh chan bool mu sync.RWMutex watchers uint @@ -58,6 +59,7 @@ func NewEngineWithConfig(cfg *Config, debugMode bool) (*Engine, error) { buildRunStopCh: make(chan bool, 1), binStopCh: make(chan bool), exitCh: make(chan bool), + procKilledCh: make(chan bool), fileChecksums: &checksumMap{m: make(map[string]string)}, watchers: 0, } @@ -509,11 +511,13 @@ func (e *Engine) runBin() error { } cmdBinPath := cmdPath(e.config.rel(e.config.binPath())) if _, err = os.Stat(cmdBinPath); os.IsNotExist(err) { + e.procKilledCh <- true return } if err = os.Remove(cmdBinPath); err != nil { e.mainLog("failed to remove %s, error: %s", e.config.rel(e.config.binPath()), err) } + e.procKilledCh <- true } e.runnerLog("running...") @@ -621,7 +625,8 @@ func (e *Engine) cleanup() { } e.mainDebug("waiting for exit...") - + <-e.procKilledCh + close(e.procKilledCh) e.running = false e.mainDebug("exited") } From d1a45b8f2a831d21473ebacd8ed552769ead84f5 Mon Sep 17 00:00:00 2001 From: Seth Brasile Date: Mon, 29 Apr 2024 20:19:42 -0500 Subject: [PATCH 2/8] signal to procKilledChan in existing deferred func --- runner/engine.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runner/engine.go b/runner/engine.go index a02e2c6e..0c1c5294 100644 --- a/runner/engine.go +++ b/runner/engine.go @@ -497,6 +497,7 @@ func (e *Engine) runBin() error { e.mainDebug("trying to kill pid %d, cmd %+v", cmd.Process.Pid, cmd.Args) defer func() { + e.procKilledCh <- true stdout.Close() stderr.Close() }() @@ -511,13 +512,11 @@ func (e *Engine) runBin() error { } cmdBinPath := cmdPath(e.config.rel(e.config.binPath())) if _, err = os.Stat(cmdBinPath); os.IsNotExist(err) { - e.procKilledCh <- true return } if err = os.Remove(cmdBinPath); err != nil { e.mainLog("failed to remove %s, error: %s", e.config.rel(e.config.binPath()), err) } - e.procKilledCh <- true } e.runnerLog("running...") From 240f4c13bf296a39bdddee06f26571acd26f80a4 Mon Sep 17 00:00:00 2001 From: Seth Brasile Date: Tue, 30 Apr 2024 01:41:31 -0500 Subject: [PATCH 3/8] without this can't exit when non zero exit code it seems like `os.Exit(state.ExitCode())` belongs here... but unclear on intent of `e.config.Build.Rerun` This at least lets you ctrl+c out of error condition instead of killing pid via external means. --- runner/engine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runner/engine.go b/runner/engine.go index 0c1c5294..5e83d4fe 100644 --- a/runner/engine.go +++ b/runner/engine.go @@ -574,6 +574,8 @@ func (e *Engine) runBin() error { e.runnerLog("Process Exit with Code: %v", state.ExitCode()) } + e.procKilledCh <- true + if !e.config.Build.Rerun { return } From 1f7825115f0de6db6ea30dca146217c7f0f63f26 Mon Sep 17 00:00:00 2001 From: Seth Brasile Date: Tue, 30 Apr 2024 02:07:20 -0500 Subject: [PATCH 4/8] Revert "without this can't exit when non zero exit code" This reverts commit fec348c04b755ff4571880191692081bc6b81841. --- runner/engine.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/runner/engine.go b/runner/engine.go index 5e83d4fe..0c1c5294 100644 --- a/runner/engine.go +++ b/runner/engine.go @@ -574,8 +574,6 @@ func (e *Engine) runBin() error { e.runnerLog("Process Exit with Code: %v", state.ExitCode()) } - e.procKilledCh <- true - if !e.config.Build.Rerun { return } From d312f15d44d6a9c7d6ed92393005d8ac245be989 Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 23 Jun 2024 22:26:14 +0800 Subject: [PATCH 5/8] use cleanup --- go.mod | 2 +- runner/engine.go | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index b1442118..32a1cb6c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/air-verse/air -go 1.22 +go 1.21 require ( dario.cat/mergo v1.0.0 diff --git a/runner/engine.go b/runner/engine.go index 0c1c5294..637b34ba 100644 --- a/runner/engine.go +++ b/runner/engine.go @@ -30,8 +30,8 @@ type Engine struct { buildRunStopCh chan bool binStopCh chan bool exitCh chan bool - procKilledCh chan bool + cleanupWg sync.WaitGroup mu sync.RWMutex watchers uint fileChecksums *checksumMap @@ -59,7 +59,6 @@ func NewEngineWithConfig(cfg *Config, debugMode bool) (*Engine, error) { buildRunStopCh: make(chan bool, 1), binStopCh: make(chan bool), exitCh: make(chan bool), - procKilledCh: make(chan bool), fileChecksums: &checksumMap{m: make(map[string]string)}, watchers: 0, } @@ -480,6 +479,7 @@ func (e *Engine) runPostCmd() error { } func (e *Engine) runBin() error { + e.cleanupWg.Add(1) killFunc := func(cmd *exec.Cmd, stdout io.ReadCloser, stderr io.ReadCloser, killCh chan struct{}, processExit chan struct{}, wg *sync.WaitGroup) { defer wg.Done() select { @@ -497,7 +497,6 @@ func (e *Engine) runBin() error { e.mainDebug("trying to kill pid %d, cmd %+v", cmd.Process.Pid, cmd.Args) defer func() { - e.procKilledCh <- true stdout.Close() stderr.Close() }() @@ -573,6 +572,7 @@ func (e *Engine) runBin() error { default: e.runnerLog("Process Exit with Code: %v", state.ExitCode()) } + e.cleanupWg.Done() if !e.config.Build.Rerun { return @@ -624,8 +624,7 @@ func (e *Engine) cleanup() { } e.mainDebug("waiting for exit...") - <-e.procKilledCh - close(e.procKilledCh) + e.cleanupWg.Wait() e.running = false e.mainDebug("exited") } From cf8e7970d6b79d562f23bbb340f84d991fbd5318 Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 23 Jun 2024 23:03:10 +0800 Subject: [PATCH 6/8] fix test case rerun --- runner/engine.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runner/engine.go b/runner/engine.go index 637b34ba..b95dfe39 100644 --- a/runner/engine.go +++ b/runner/engine.go @@ -31,7 +31,7 @@ type Engine struct { binStopCh chan bool exitCh chan bool - cleanupWg sync.WaitGroup + procKillWg sync.WaitGroup mu sync.RWMutex watchers uint fileChecksums *checksumMap @@ -479,7 +479,6 @@ func (e *Engine) runPostCmd() error { } func (e *Engine) runBin() error { - e.cleanupWg.Add(1) killFunc := func(cmd *exec.Cmd, stdout io.ReadCloser, stderr io.ReadCloser, killCh chan struct{}, processExit chan struct{}, wg *sync.WaitGroup) { defer wg.Done() select { @@ -538,6 +537,7 @@ func (e *Engine) runBin() error { case <-killCh: return default: + e.procKillWg.Add(1) command := strings.Join(append([]string{e.config.Build.Bin}, e.runArgs...), " ") cmd, stdout, stderr, _ := e.startCmd(command) processExit := make(chan struct{}) @@ -572,7 +572,7 @@ func (e *Engine) runBin() error { default: e.runnerLog("Process Exit with Code: %v", state.ExitCode()) } - e.cleanupWg.Done() + e.procKillWg.Done() if !e.config.Build.Rerun { return @@ -624,7 +624,7 @@ func (e *Engine) cleanup() { } e.mainDebug("waiting for exit...") - e.cleanupWg.Wait() + e.procKillWg.Wait() e.running = false e.mainDebug("exited") } From 360714a021b1b77e50a5656fefc4f8bb9312d328 Mon Sep 17 00:00:00 2001 From: xiantang Date: Sun, 23 Jun 2024 23:06:42 +0800 Subject: [PATCH 7/8] upgrade gomod version --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 32a1cb6c..b1442118 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/air-verse/air -go 1.21 +go 1.22 require ( dario.cat/mergo v1.0.0 From 849e0290b41bdd22396ee24ad0aa8c8ad9b7f60c Mon Sep 17 00:00:00 2001 From: xiantang Date: Mon, 24 Jun 2024 22:04:22 +0800 Subject: [PATCH 8/8] remove unused `WaitGroup` --- runner/engine.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/runner/engine.go b/runner/engine.go index b95dfe39..0a69fa10 100644 --- a/runner/engine.go +++ b/runner/engine.go @@ -479,8 +479,7 @@ func (e *Engine) runPostCmd() error { } func (e *Engine) runBin() error { - killFunc := func(cmd *exec.Cmd, stdout io.ReadCloser, stderr io.ReadCloser, killCh chan struct{}, processExit chan struct{}, wg *sync.WaitGroup) { - defer wg.Done() + killFunc := func(cmd *exec.Cmd, stdout io.ReadCloser, stderr io.ReadCloser, killCh chan struct{}, processExit chan struct{}) { select { // listen to binStopCh // cleanup() will close binStopCh when engine stop @@ -519,13 +518,11 @@ func (e *Engine) runBin() error { e.runnerLog("running...") go func() { - wg := sync.WaitGroup{} defer func() { select { case <-e.exitCh: e.mainDebug("exit in runBin") - wg.Wait() default: } }() @@ -546,11 +543,10 @@ func (e *Engine) runBin() error { e.proxy.Reload() } - wg.Add(1) e.withLock(func() { close(e.binStopCh) e.binStopCh = make(chan bool) - go killFunc(cmd, stdout, stderr, killCh, processExit, &wg) + go killFunc(cmd, stdout, stderr, killCh, processExit) }) go func() {