diff --git a/agent.go b/agent.go index 109b2b7eeb..6881d4f31c 100644 --- a/agent.go +++ b/agent.go @@ -69,7 +69,7 @@ type sandbox struct { grpcListener net.Listener sharedPidNs namespace mounts []string - subreaper *reaper + subreaper reaper } type namespace struct { @@ -575,6 +575,9 @@ func main() { os.Exit(exitSuccess) }() + r := &agentReaper{} + r.init() + // Initialize unique sandbox structure. s := &sandbox{ containers: make(map[string]*container), @@ -582,9 +585,7 @@ func main() { // pivot_root won't work for init, see // Documention/filesystem/ramfs-rootfs-initramfs.txt noPivotRoot: os.Getpid() == 1, - subreaper: &reaper{ - exitCodeChans: make(map[int]chan<- int), - }, + subreaper: r, } if err = s.initLogger(); err != nil { diff --git a/grpc.go b/grpc.go index 5692a75753..247280221c 100644 --- a/grpc.go +++ b/grpc.go @@ -206,8 +206,8 @@ func (a *agentGRPC) execProcess(ctr *container, proc *process, createContainer b // miss the opportunity to get the exit code, leading WaitProcess() to // wait forever on the new channel. // This lock has to be taken before we run the new process. - a.sandbox.subreaper.RLock() - defer a.sandbox.subreaper.RUnlock() + a.sandbox.subreaper.lock() + defer a.sandbox.subreaper.unlock() if createContainer { err = ctr.container.Start(&proc.process) diff --git a/reaper.go b/reaper.go index 12ad0f70f0..5289c8d3a4 100644 --- a/reaper.go +++ b/reaper.go @@ -18,7 +18,19 @@ import ( grpcStatus "google.golang.org/grpc/status" ) -type reaper struct { +type reaper interface { + init() + getExitCodeCh(pid int) (chan<- int, error) + setExitCodeCh(pid int, exitCodeCh chan<- int) + deleteExitCodeCh(pid int) + reap() error + start(c *exec.Cmd) (<-chan int, error) + wait(exitCodeCh <-chan int, proc waitProcess) (int, error) + lock() + unlock() +} + +type agentReaper struct { sync.RWMutex chansLock sync.RWMutex @@ -33,7 +45,19 @@ func exitStatus(status unix.WaitStatus) int { return status.ExitStatus() } -func (r *reaper) getExitCodeCh(pid int) (chan<- int, error) { +func (r *agentReaper) init() { + r.exitCodeChans = make(map[int]chan<- int) +} + +func (r *agentReaper) lock() { + r.RLock() +} + +func (r *agentReaper) unlock() { + r.RUnlock() +} + +func (r *agentReaper) getExitCodeCh(pid int) (chan<- int, error) { r.chansLock.RLock() defer r.chansLock.RUnlock() @@ -45,21 +69,21 @@ func (r *reaper) getExitCodeCh(pid int) (chan<- int, error) { return exitCodeCh, nil } -func (r *reaper) setExitCodeCh(pid int, exitCodeCh chan<- int) { +func (r *agentReaper) setExitCodeCh(pid int, exitCodeCh chan<- int) { r.chansLock.Lock() defer r.chansLock.Unlock() r.exitCodeChans[pid] = exitCodeCh } -func (r *reaper) deleteExitCodeCh(pid int) { +func (r *agentReaper) deleteExitCodeCh(pid int) { r.chansLock.Lock() defer r.chansLock.Unlock() delete(r.exitCodeChans, pid) } -func (r *reaper) reap() error { +func (r *agentReaper) reap() error { var ( ws unix.WaitStatus rus unix.Rusage @@ -119,7 +143,7 @@ func (r *reaper) reap() error { // start starts the exec command and registers the process to the reaper. // This function is a helper for exec.Cmd.Start() since this needs to be // in sync with exec.Cmd.Wait(). -func (r *reaper) start(c *exec.Cmd) (<-chan int, error) { +func (r *agentReaper) start(c *exec.Cmd) (<-chan int, error) { // This lock is very important to avoid any race with reaper.reap(). // We don't want the reaper to reap a process before we have added // it to the exit code channel list. @@ -143,7 +167,7 @@ func (r *reaper) start(c *exec.Cmd) (<-chan int, error) { // from the subreaper, the exit code is sent through the provided channel. // This function is a helper for exec.Cmd.Wait() and os.Process.Wait() since // both cannot be used directly, because of the subreaper. -func (r *reaper) wait(exitCodeCh <-chan int, proc waitProcess) (int, error) { +func (r *agentReaper) wait(exitCodeCh <-chan int, proc waitProcess) (int, error) { // Wait for the subreaper to receive the SIGCHLD signal. Once it gets // it, this channel will be notified by receiving the exit code of the // corresponding process.