linux: Prevent deadlock in reaper.WaitPid()
A deadlock can occurs if `WaitPid()` is called twice before the process dies. Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
parent
9923a49e97
commit
eb4abac9f7
@ -88,7 +88,7 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*sh
|
||||
s.processes[r.ID] = process
|
||||
s.mu.Unlock()
|
||||
cmd := &reaper.Cmd{
|
||||
ExitCh: make(chan int, 1),
|
||||
ExitCh: make(chan struct{}),
|
||||
}
|
||||
reaper.Default.Register(pid, cmd)
|
||||
s.events <- &eventsapi.TaskCreate{
|
||||
@ -126,7 +126,7 @@ func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi.
|
||||
} else {
|
||||
pid := p.Pid()
|
||||
cmd := &reaper.Cmd{
|
||||
ExitCh: make(chan int, 1),
|
||||
ExitCh: make(chan struct{}),
|
||||
}
|
||||
reaper.Default.Register(pid, cmd)
|
||||
go s.waitExit(p, pid, cmd)
|
||||
@ -366,7 +366,7 @@ func (s *Service) Update(ctx context.Context, r *shimapi.UpdateTaskRequest) (*go
|
||||
}
|
||||
|
||||
func (s *Service) waitExit(p process, pid int, cmd *reaper.Cmd) {
|
||||
status := <-cmd.ExitCh
|
||||
status, _ := reaper.Default.WaitPid(pid)
|
||||
p.SetExited(status)
|
||||
|
||||
reaper.Default.Delete(pid)
|
||||
|
@ -4,7 +4,6 @@ package reaper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"sync"
|
||||
|
||||
@ -12,6 +11,10 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoSuchProcess = errors.New("no such process")
|
||||
)
|
||||
|
||||
// Reap should be called when the process receives an SIGCHLD. Reap will reap
|
||||
// all exited processes and close their wait channels
|
||||
func Reap() error {
|
||||
@ -30,7 +33,8 @@ func Reap() error {
|
||||
// pipes are closed and finalizers are run on the process
|
||||
c.c.Wait()
|
||||
}
|
||||
c.ExitCh <- e.Status
|
||||
c.exitStatus = e.Status
|
||||
close(c.ExitCh)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -68,7 +72,7 @@ func (m *Monitor) CombinedOutput(c *exec.Cmd) ([]byte, error) {
|
||||
func (m *Monitor) Start(c *exec.Cmd) error {
|
||||
rc := &Cmd{
|
||||
c: c,
|
||||
ExitCh: make(chan int, 1),
|
||||
ExitCh: make(chan struct{}),
|
||||
}
|
||||
// start the process
|
||||
m.Lock()
|
||||
@ -105,7 +109,8 @@ func (m *Monitor) RegisterNL(pid int, c *Cmd) {
|
||||
if status, ok := m.unknown[pid]; ok {
|
||||
delete(m.unknown, pid)
|
||||
m.cmds[pid] = c
|
||||
c.ExitCh <- status
|
||||
c.exitStatus = status
|
||||
close(c.ExitCh)
|
||||
return
|
||||
}
|
||||
m.cmds[pid] = c
|
||||
@ -116,13 +121,13 @@ func (m *Monitor) WaitPid(pid int) (int, error) {
|
||||
rc, ok := m.cmds[pid]
|
||||
m.Unlock()
|
||||
if !ok {
|
||||
return 255, fmt.Errorf("process does not exist")
|
||||
return 255, errors.Wrapf(ErrNoSuchProcess, "pid %d", pid)
|
||||
}
|
||||
ec := <-rc.ExitCh
|
||||
if ec != 0 {
|
||||
return ec, errors.Errorf("exit status %d", ec)
|
||||
<-rc.ExitCh
|
||||
if rc.exitStatus != 0 {
|
||||
return rc.exitStatus, errors.Errorf("exit status %d", rc.exitStatus)
|
||||
}
|
||||
return ec, nil
|
||||
return rc.exitStatus, nil
|
||||
}
|
||||
|
||||
// Command returns the registered pid for the command created
|
||||
@ -139,6 +144,7 @@ func (m *Monitor) Delete(pid int) {
|
||||
}
|
||||
|
||||
type Cmd struct {
|
||||
c *exec.Cmd
|
||||
ExitCh chan int
|
||||
c *exec.Cmd
|
||||
ExitCh chan struct{}
|
||||
exitStatus int
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user