From 6ee8577e54fb15730effb7e44340eed2e70ed42f Mon Sep 17 00:00:00 2001 From: Wei Fu Date: Sun, 31 Oct 2021 23:04:04 +0800 Subject: [PATCH] sys/reaper: avoid leaky goroutine when exec timeout The channel is created with no capacity that it needs receiver when sending data. Otherwise, the sending-data goroutine will be blocked forever. For the #6166 pr, the exec command timeout will return and no receiver for the data. It will cause goroutine leaky. This commit allocates buffered channel for the command status and closes the channel after sending. And also use time.Timer with Stop for performance concern. Signed-off-by: Wei Fu --- sys/reaper/reaper_unix.go | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/sys/reaper/reaper_unix.go b/sys/reaper/reaper_unix.go index 54cfe9eed..a40232606 100644 --- a/sys/reaper/reaper_unix.go +++ b/sys/reaper/reaper_unix.go @@ -119,23 +119,33 @@ func (m *Monitor) Wait(c *exec.Cmd, ec chan runc.Exit) (int, error) { // WaitTimeout is used to skip the blocked command and kill the left process. func (m *Monitor) WaitTimeout(c *exec.Cmd, ec chan runc.Exit, timeout time.Duration) (int, error) { - sch := make(chan int) - ech := make(chan error) + type exitStatusWrapper struct { + status int + err error + } + + // capacity can make sure that the following goroutine will not be + // blocked if there is no receiver when timeout. + waitCh := make(chan *exitStatusWrapper, 1) go func() { + defer close(waitCh) + status, err := m.Wait(c, ec) - sch <- status - if err != nil { - ech <- err + waitCh <- &exitStatusWrapper{ + status: status, + err: err, } }() + + timer := time.NewTimer(timeout) + defer timer.Stop() + select { - case <-time.After(timeout): + case <-timer.C: syscall.Kill(c.Process.Pid, syscall.SIGKILL) - return 0, errors.Errorf("timeout %ds for cmd(pid=%d): %s, %s", timeout/time.Second, c.Process.Pid, c.Path, c.Args) - case status := <-sch: - return status, nil - case err := <-ech: - return -1, err + return 0, errors.Errorf("timeout %v for cmd(pid=%d): %s, %s", timeout, c.Process.Pid, c.Path, c.Args) + case res := <-waitCh: + return res.status, res.err } }