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 <fuweid89@gmail.com>
This commit is contained in:
Wei Fu 2021-10-31 23:04:04 +08:00
parent d0bdb0b65e
commit 6ee8577e54

View File

@ -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. // 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) { func (m *Monitor) WaitTimeout(c *exec.Cmd, ec chan runc.Exit, timeout time.Duration) (int, error) {
sch := make(chan int) type exitStatusWrapper struct {
ech := make(chan error) 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() { go func() {
defer close(waitCh)
status, err := m.Wait(c, ec) status, err := m.Wait(c, ec)
sch <- status waitCh <- &exitStatusWrapper{
if err != nil { status: status,
ech <- err err: err,
} }
}() }()
timer := time.NewTimer(timeout)
defer timer.Stop()
select { select {
case <-time.After(timeout): case <-timer.C:
syscall.Kill(c.Process.Pid, syscall.SIGKILL) 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) return 0, errors.Errorf("timeout %v for cmd(pid=%d): %s, %s", timeout, c.Process.Pid, c.Path, c.Args)
case status := <-sch: case res := <-waitCh:
return status, nil return res.status, res.err
case err := <-ech:
return -1, err
} }
} }