From bc473d354cab3389af93b9f5c2c40a42f0ca5185 Mon Sep 17 00:00:00 2001 From: John Eikenberry Date: Wed, 22 Apr 2020 19:01:22 -0700 Subject: [PATCH] fix data race b/w start and kill Racing over saving and checking ProcessState. It was checking ProcessState to see if the command had exited. I tweaked exitCh to have closing it indicate that the command had exited. --- child/child.go | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/child/child.go b/child/child.go index 3c94816f5..1b6742707 100644 --- a/child/child.go +++ b/child/child.go @@ -290,14 +290,14 @@ func (c *Child) start() error { // down the exit channel. c.stopLock.RLock() defer c.stopLock.RUnlock() - if c.stopped { - return + if !c.stopped { + select { + case <-c.stopCh: + case exitCh <- code: + } } - select { - case <-c.stopCh: - case exitCh <- code: - } + close(exitCh) }() c.exitCh = exitCh @@ -365,16 +365,13 @@ func (c *Child) reload() error { return c.signal(c.reloadSignal) } +// kill sends the signal to kill the process using the configured signal +// if set, else the default system signal func (c *Child) kill(immediately bool) { - if !c.running() { - return - } - exited := false - process := c.cmd.Process - - if c.cmd.ProcessState != nil { + if !c.running() { log.Printf("[DEBUG] (child) Kill() called but process dead; not waiting for splay.") + return } else if immediately { log.Printf("[DEBUG] (child) Kill() called but performing immediate shutdown; not waiting for splay.") } else { @@ -384,6 +381,9 @@ func (c *Child) kill(immediately bool) { } } + exited := false + process := c.cmd.Process + if c.killSignal != nil { if err := process.Signal(c.killSignal); err == nil { // Wait a few seconds for it to exit @@ -410,6 +410,11 @@ func (c *Child) kill(immediately bool) { } func (c *Child) running() bool { + select { + case <-c.exitCh: + return false + default: + } return c.cmd != nil && c.cmd.Process != nil }