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