Browse Source

Fix race on sending stdin close event

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Tonis Tiigi 8 years ago
parent
commit
4e262f6387
2 changed files with 35 additions and 1 deletions
  1. 30 0
      integration-cli/docker_cli_run_test.go
  2. 5 1
      libcontainerd/container_unix.go

+ 30 - 0
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
+}

+ 5 - 1
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:
 				}
 			}()
 		})