mirror of
https://github.com/containers/podman.git
synced 2025-06-23 02:18:13 +08:00
Merge pull request #1833 from giuseppe/remove-exec-polling
exec: remove polling for PID file
This commit is contained in:
@ -328,6 +328,11 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error exec %s", c.ID())
|
||||
}
|
||||
chWait := make(chan error)
|
||||
go func() {
|
||||
chWait <- execCmd.Wait()
|
||||
}()
|
||||
defer close(chWait)
|
||||
|
||||
pidFile := c.execPidPath(sessionID)
|
||||
// 60 second seems a reasonable time to wait
|
||||
@ -336,18 +341,12 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
|
||||
const pidWaitTimeout = 60000
|
||||
|
||||
// Wait until the runtime makes the pidfile
|
||||
// TODO: If runtime errors before the PID file is created, we have to
|
||||
// wait for timeout here
|
||||
if err := WaitForFile(pidFile, pidWaitTimeout*time.Millisecond); err != nil {
|
||||
logrus.Debugf("Timed out waiting for pidfile from runtime for container %s exec", c.ID())
|
||||
|
||||
// Check if an error occurred in the process before we made a pidfile
|
||||
// TODO: Wait() here is a poor choice - is there a way to see if
|
||||
// a process has finished, instead of waiting for it to finish?
|
||||
if err := execCmd.Wait(); err != nil {
|
||||
exited, err := WaitForFile(pidFile, chWait, pidWaitTimeout*time.Millisecond)
|
||||
if err != nil {
|
||||
if exited {
|
||||
// If the runtime exited, propagate the error we got from the process.
|
||||
return err
|
||||
}
|
||||
|
||||
return errors.Wrapf(err, "timed out waiting for runtime to create pidfile for exec session in container %s", c.ID())
|
||||
}
|
||||
|
||||
@ -389,7 +388,10 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
|
||||
locked = false
|
||||
}
|
||||
|
||||
waitErr := execCmd.Wait()
|
||||
var waitErr error
|
||||
if !exited {
|
||||
waitErr = <-chWait
|
||||
}
|
||||
|
||||
// Lock again
|
||||
if !c.batched {
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/containers/image/signature"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -90,31 +91,64 @@ func MountExists(specMounts []spec.Mount, dest string) bool {
|
||||
}
|
||||
|
||||
// WaitForFile waits until a file has been created or the given timeout has occurred
|
||||
func WaitForFile(path string, timeout time.Duration) error {
|
||||
func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, error) {
|
||||
done := make(chan struct{})
|
||||
chControl := make(chan struct{})
|
||||
|
||||
var inotifyEvents chan fsnotify.Event
|
||||
var timer chan struct{}
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err == nil {
|
||||
if err := watcher.Add(filepath.Dir(path)); err == nil {
|
||||
inotifyEvents = watcher.Events
|
||||
}
|
||||
defer watcher.Close()
|
||||
}
|
||||
if inotifyEvents == nil {
|
||||
// If for any reason we fail to create the inotify
|
||||
// watcher, fallback to polling the file
|
||||
timer = make(chan struct{})
|
||||
go func() {
|
||||
select {
|
||||
case <-chControl:
|
||||
close(timer)
|
||||
return
|
||||
default:
|
||||
time.Sleep(25 * time.Millisecond)
|
||||
timer <- struct{}{}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-chControl:
|
||||
return
|
||||
default:
|
||||
case <-timer:
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
case <-inotifyEvents:
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
time.Sleep(25 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case e := <-chWait:
|
||||
return true, e
|
||||
case <-done:
|
||||
return nil
|
||||
return false, nil
|
||||
case <-time.After(timeout):
|
||||
close(chControl)
|
||||
return errors.Wrapf(ErrInternal, "timed out waiting for file %s", path)
|
||||
return false, errors.Wrapf(ErrInternal, "timed out waiting for file %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user