From 972c89493148f930388097816663d453bf3e8c2c Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 11 Aug 2014 15:00:55 -0700 Subject: [PATCH] Improve wait during restart We need to do this so that when a user asks docker to stop the container and it is currently in the restart loop we don't want to have to wait for the duration of the restart time increment before ack. the stop. Signed-off-by: Michael Crosby --- daemon/monitor.go | 50 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/daemon/monitor.go b/daemon/monitor.go index 6c6c7b76db..d9e67b0681 100644 --- a/daemon/monitor.go +++ b/daemon/monitor.go @@ -35,6 +35,11 @@ type containerMonitor struct { // either because docker or the user asked for the container to be stopped shouldStop bool + // stopChan is used to signal to the monitor whenever there is a wait for the + // next restart so that the timeIncrement is not honored and the user is not + // left waiting for nothing to happen during this time + stopChan chan struct{} + // timeIncrement is the amount of time to wait between restarts // this is in milliseconds timeIncrement int @@ -45,6 +50,7 @@ func newContainerMonitor(container *Container, policy runconfig.RestartPolicy) * container: container, restartPolicy: policy, timeIncrement: defaultTimeIncrement, + stopChan: make(chan struct{}, 1), } } @@ -52,7 +58,14 @@ func newContainerMonitor(container *Container, policy runconfig.RestartPolicy) * // for exits the next time the process dies func (m *containerMonitor) ExitOnNext() { m.mux.Lock() - m.shouldStop = true + + // we need to protect having a double close of the channel when stop is called + // twice or else we will get a panic + if !m.shouldStop { + m.shouldStop = true + close(m.stopChan) + } + m.mux.Unlock() } @@ -87,7 +100,7 @@ func (m *containerMonitor) Start() error { // reset the restart count m.container.RestartCount = -1 - for !m.shouldStop { + for { m.container.RestartCount++ if err := m.container.startLoggingToDisk(); err != nil { @@ -109,22 +122,34 @@ func (m *containerMonitor) Start() error { if m.shouldRestart(exitStatus) { m.container.State.SetRestarting(exitStatus) + m.container.LogEvent("die") + m.resetContainer() // sleep with a small time increment between each restart to help avoid issues cased by quickly // restarting the container because of some types of errors ( networking cut out, etc... ) - time.Sleep(time.Duration(m.timeIncrement) * time.Millisecond) + m.waitForNextRestart() + + // we need to check this before reentering the loop because the waitForNextRestart could have + // been terminated by a request from a user + if m.shouldStop { + m.container.State.SetStopped(exitStatus) + + return err + } continue } + m.container.State.SetStopped(exitStatus) + + m.container.LogEvent("die") + + m.resetContainer() + break } - m.container.State.SetStopped(exitStatus) - - m.resetContainer() - return err } @@ -145,6 +170,15 @@ func (m *containerMonitor) resetMonitor(successful bool) { } } +// waitForNextRestart waits with the default time increment to restart the container unless +// a user or docker asks to container to be stopped +func (m *containerMonitor) waitForNextRestart() { + select { + case <-time.After(time.Duration(m.timeIncrement) * time.Millisecond): + case <-m.stopChan: + } +} + // shouldRestart checks the restart policy and applies the rules to determine if // the container's process should be restarted func (m *containerMonitor) shouldRestart(exitStatus int) bool { @@ -221,8 +255,6 @@ func (m *containerMonitor) resetContainer() { container.stdin, container.stdinPipe = io.Pipe() } - container.LogEvent("die") - c := container.command.Cmd container.command.Cmd = exec.Cmd{