diff options
author | Nicolas Lacasse <nlacasse@google.com> | 2018-11-15 15:34:38 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-11-15 15:35:41 -0800 |
commit | adf8138e069a99aa7f3fe190a9a7c17a4e88b99a (patch) | |
tree | 349dd842162121546b957702aa8abebdfc82cc7b /runsc/test/testutil/testutil.go | |
parent | f7aa9371247a3e7d8c490ac0fd4c4f3ff6de2017 (diff) |
Allow sandbox.Wait to be called after the sandbox has exited.
sandbox.Wait is racey, as the sandbox may have exited before it is called, or
even during.
We already had code to handle the case that the sandbox exits during the Wait
call, but we were not properly handling the case where the sandbox has exited
before the call.
The best we can do in such cases is return the sandbox exit code as the
application exit code.
PiperOrigin-RevId: 221702517
Change-Id: I290d0333cc094c7c1c3b4ce0f17f61a3e908d787
Diffstat (limited to 'runsc/test/testutil/testutil.go')
-rw-r--r-- | runsc/test/testutil/testutil.go | 61 |
1 files changed, 51 insertions, 10 deletions
diff --git a/runsc/test/testutil/testutil.go b/runsc/test/testutil/testutil.go index c816de3f0..7a17d0552 100644 --- a/runsc/test/testutil/testutil.go +++ b/runsc/test/testutil/testutil.go @@ -29,6 +29,7 @@ import ( "path/filepath" "runtime" "strings" + "sync" "sync/atomic" "syscall" "time" @@ -296,18 +297,37 @@ func RunAsRoot() { os.Exit(0) } -// StartReaper starts a goroutine that will reap all children processes created -// by the tests. Caller must call the returned function to stop it. -func StartReaper() func() { - ch := make(chan os.Signal, 1) - signal.Notify(ch, syscall.SIGCHLD) - stop := make(chan struct{}) +// Reaper reaps child processes. +type Reaper struct { + // mu protects ch, which will be nil if the reaper is not running. + mu sync.Mutex + ch chan os.Signal +} + +// Start starts reaping child processes. +func (r *Reaper) Start() { + r.mu.Lock() + defer r.mu.Unlock() + + if r.ch != nil { + panic("reaper.Start called on a running reaper") + } + + r.ch = make(chan os.Signal, 1) + signal.Notify(r.ch, syscall.SIGCHLD) go func() { for { - select { - case <-ch: - case <-stop: + r.mu.Lock() + ch := r.ch + r.mu.Unlock() + if ch == nil { + return + } + + _, ok := <-ch + if !ok { + // Channel closed. return } for { @@ -318,7 +338,28 @@ func StartReaper() func() { } } }() - return func() { stop <- struct{}{} } +} + +// Stop stops reaping child processes. +func (r *Reaper) Stop() { + r.mu.Lock() + defer r.mu.Unlock() + + if r.ch == nil { + panic("reaper.Stop called on a stopped reaper") + } + + signal.Stop(r.ch) + close(r.ch) + r.ch = nil +} + +// StartReaper is a helper that starts a new Reaper and returns a function to +// stop it. +func StartReaper() func() { + r := &Reaper{} + r.Start() + return r.Stop } // RetryEintr retries the function until an error different than EINTR is |