From c498d4700df8216ccefd6d2f03b5ebe6d6e9fd47 Mon Sep 17 00:00:00 2001 From: Zhang Wei Date: Tue, 17 May 2016 10:30:06 +0800 Subject: [PATCH] Bug fix: `docker run -i --restart always` hangs. e.g. ``` $ docker run -i --restart always busybox sh pwd / exit 11 <...hang...> ``` This is because Attach(daemon side) and Run(client side) both hangs on WaitStop, if container is restarted too quickly, wait won't have chance to get exit signal. Signed-off-by: Zhang Wei --- api/client/container/run.go | 2 +- daemon/attach.go | 11 ++++++++- integration-cli/docker_cli_run_test.go | 31 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/api/client/container/run.go b/api/client/container/run.go index e0629d0621..5de2bd3f19 100644 --- a/api/client/container/run.go +++ b/api/client/container/run.go @@ -287,7 +287,7 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, } } else { // No Autoremove: Simply retrieve the exit code - if !config.Tty { + if !config.Tty && hostConfig.RestartPolicy.IsNone() { // In non-TTY mode, we can't detach, so we must wait for container exit if status, err = client.ContainerWait(ctx, createResponse.ID); err != nil { return err diff --git a/daemon/attach.go b/daemon/attach.go index eb263ac7f7..0898a00d20 100644 --- a/daemon/attach.go +++ b/daemon/attach.go @@ -119,6 +119,15 @@ func (daemon *Daemon) containerAttach(c *container.Container, stdin io.ReadClose }() stdinPipe = r } + + waitChan := make(chan struct{}) + if c.Config.StdinOnce && !c.Config.Tty { + go func() { + c.WaitStop(-1 * time.Second) + close(waitChan) + }() + } + err := <-c.Attach(stdinPipe, stdout, stderr, keys) if err != nil { if _, ok := err.(container.DetachError); ok { @@ -131,7 +140,7 @@ func (daemon *Daemon) containerAttach(c *container.Container, stdin io.ReadClose // If we are in stdinonce mode, wait for the process to end // otherwise, simply return if c.Config.StdinOnce && !c.Config.Tty { - c.WaitStop(-1 * time.Second) + <-waitChan } } return nil diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index e5dbe0f77e..3375269fbb 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -1828,6 +1828,37 @@ func (s *DockerSuite) TestRunExitOnStdinClose(c *check.C) { } } +// Test run -i --restart xxx doesn't hang +func (s *DockerSuite) TestRunInteractiveWithRestartPolicy(c *check.C) { + name := "test-inter-restart" + runCmd := exec.Command(dockerBinary, "run", "-i", "--name", name, "--restart=always", "busybox", "sh") + + stdin, err := runCmd.StdinPipe() + c.Assert(err, checker.IsNil) + + err = runCmd.Start() + c.Assert(err, checker.IsNil) + c.Assert(waitRun(name), check.IsNil) + + _, err = stdin.Write([]byte("exit 11\n")) + c.Assert(err, checker.IsNil) + + finish := make(chan error) + go func() { + finish <- runCmd.Wait() + close(finish) + }() + delay := 10 * time.Second + select { + case <-finish: + case <-time.After(delay): + c.Fatal("run -i --restart hangs") + } + + c.Assert(waitRun(name), check.IsNil) + dockerCmd(c, "stop", name) +} + // Test for #2267 func (s *DockerSuite) TestRunWriteHostsFileAndNotCommit(c *check.C) { // Cannot run on Windows as Windows does not support diff.