From 3f0aa9bcebac6ca74f62a0005636d6ec1ee67210 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Mon, 21 Nov 2016 14:10:34 -0800 Subject: [PATCH] Fix race on sending stdin close event Signed-off-by: Tonis Tiigi (cherry picked from commit 4e262f63876018ca78d54a98eee3f533352b0ac9) Signed-off-by: Victor Vieux --- integration-cli/docker_cli_run_test.go | 30 ++++++++++++++++++++++++++ libcontainerd/container_unix.go | 6 +++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 81c4d096cc..033d03a6b9 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -5,6 +5,7 @@ import ( "bytes" "encoding/json" "fmt" + "io" "io/ioutil" "net" "os" @@ -4849,3 +4850,32 @@ func (s *DockerSuite) TestRunEmptyEnv(c *check.C) { c.Assert(err, checker.NotNil) c.Assert(out, checker.Contains, expectedOutput) } + +// #28658 +func (s *DockerSuite) TestSlowStdinClosing(c *check.C) { + name := "testslowstdinclosing" + repeat := 3 // regression happened 50% of the time + for i := 0; i < repeat; i++ { + cmd := exec.Command(dockerBinary, "run", "--rm", "--name", name, "-i", "busybox", "cat") + cmd.Stdin = &delayedReader{} + done := make(chan error, 1) + go func() { + _, err := runCommand(cmd) + done <- err + }() + + select { + case <-time.After(15 * time.Second): + c.Fatal("running container timed out") // cleanup in teardown + case err := <-done: + c.Assert(err, checker.IsNil) + } + } +} + +type delayedReader struct{} + +func (s *delayedReader) Read([]byte) (int, error) { + time.Sleep(500 * time.Millisecond) + return 0, io.EOF +} diff --git a/libcontainerd/container_unix.go b/libcontainerd/container_unix.go index 61d2b2a547..1bd6050a72 100644 --- a/libcontainerd/container_unix.go +++ b/libcontainerd/container_unix.go @@ -116,12 +116,16 @@ func (ctr *container) start(checkpoint string, checkpointDir string, attachStdio stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed err = stdin.Close() go func() { + select { + case <-ready: + case <-ctx.Done(): + } select { case <-ready: if err := ctr.sendCloseStdin(); err != nil { logrus.Warnf("failed to close stdin: %+v", err) } - case <-ctx.Done(): + default: } }() })