Skip to content

Commit 6162d46

Browse files
committed
reuse per-proc logger across restarts to stop goroutine leak
1 parent 05f97a4 commit 6162d46

3 files changed

Lines changed: 50 additions & 1 deletion

File tree

goreman_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,47 @@ func TestGoremanReleasesRPCPort(t *testing.T) {
189189
t.Fatalf("RPC port was not released after goreman stopped: %v", err)
190190
}
191191

192+
func TestGoremanRestartDoesntLeakGoroutines(t *testing.T) {
193+
var file = []byte(`
194+
web1: sleep 10
195+
`)
196+
sc := make(chan os.Signal, 1)
197+
done := make(chan struct{}, 1)
198+
go func() {
199+
startGoreman(context.TODO(), t, sc, file)
200+
done <- struct{}{}
201+
}()
202+
waitRunning := func() {
203+
for {
204+
proc := findProc("web1")
205+
if proc != nil {
206+
proc.mu.Lock()
207+
cmd := proc.cmd
208+
proc.mu.Unlock()
209+
if cmd != nil && cmd.Process != nil {
210+
return
211+
}
212+
}
213+
time.Sleep(5 * time.Millisecond)
214+
}
215+
}
216+
waitRunning()
217+
before := runtime.NumGoroutine()
218+
for i := 0; i < 20; i++ {
219+
if err := restartProc("web1"); err != nil {
220+
t.Fatal(err)
221+
}
222+
waitRunning()
223+
}
224+
after := runtime.NumGoroutine()
225+
t.Logf("goroutines: before=%d after=%d", before, after)
226+
if after-before > 10 {
227+
t.Errorf("restarting leaked goroutines: %d before, %d after", before, after)
228+
}
229+
sc <- os.Interrupt
230+
<-done
231+
}
232+
192233
func TestGoremanStopProcDoesntStopOtherProcs(t *testing.T) {
193234
var file = []byte(`
194235
web1: sleep 10

main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ type procInfo struct {
6666
mu sync.Mutex
6767
cond *sync.Cond
6868
waitErr error
69+
70+
// logger is created on first spawn and reused across restarts so that
71+
// restarting a proc does not leak the logger goroutine.
72+
logger *clogger
6973
}
7074

7175
var mu sync.Mutex

proc.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import (
1212
// spawnProc starts the specified proc, and returns any error from running it.
1313
func spawnProc(name string, errCh chan<- error) {
1414
proc := findProc(name)
15-
logger := createLogger(name, proc.colorIndex)
15+
logger := proc.logger
16+
if logger == nil {
17+
logger = createLogger(name, proc.colorIndex)
18+
proc.logger = logger
19+
}
1620

1721
cs := append(cmdStart, proc.cmdline)
1822
cmd := exec.Command(cs[0], cs[1:]...)

0 commit comments

Comments
 (0)