Browse code

Merge pull request #16289 from cpuguy83/11957_fix_stdin_block_after_container_exit

Ensure stdin does not block after container stop

Jess Frazelle authored on 2015/09/24 07:29:48
Showing 2 changed files
... ...
@@ -16,7 +16,6 @@ import (
16 16
 	"github.com/Sirupsen/logrus"
17 17
 	"github.com/docker/docker/api"
18 18
 	"github.com/docker/docker/autogen/dockerversion"
19
-	"github.com/docker/docker/pkg/promise"
20 19
 	"github.com/docker/docker/pkg/stdcopy"
21 20
 	"github.com/docker/docker/pkg/term"
22 21
 )
... ...
@@ -186,8 +185,6 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
186 186
 		started <- rwc
187 187
 	}
188 188
 
189
-	var receiveStdout chan error
190
-
191 189
 	var oldState *term.State
192 190
 
193 191
 	if in != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" {
... ...
@@ -198,19 +195,15 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
198 198
 		defer term.RestoreTerminal(cli.inFd, oldState)
199 199
 	}
200 200
 
201
+	receiveStdout := make(chan error, 1)
201 202
 	if stdout != nil || stderr != nil {
202
-		receiveStdout = promise.Go(func() (err error) {
203
+		go func() {
203 204
 			defer func() {
204 205
 				if in != nil {
205 206
 					if setRawTerminal && cli.isTerminalIn {
206 207
 						term.RestoreTerminal(cli.inFd, oldState)
207 208
 					}
208
-					// For some reason this Close call blocks on darwin..
209
-					// As the client exists right after, simply discard the close
210
-					// until we find a better solution.
211
-					if runtime.GOOS != "darwin" {
212
-						in.Close()
213
-					}
209
+					in.Close()
214 210
 				}
215 211
 			}()
216 212
 
... ...
@@ -221,11 +214,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
221 221
 				_, err = stdcopy.StdCopy(stdout, stderr, br)
222 222
 			}
223 223
 			logrus.Debugf("[hijack] End of stdout")
224
-			return err
225
-		})
224
+			receiveStdout <- err
225
+		}()
226 226
 	}
227 227
 
228
-	sendStdin := promise.Go(func() error {
228
+	stdinDone := make(chan struct{})
229
+	go func() {
229 230
 		if in != nil {
230 231
 			io.Copy(rwc, in)
231 232
 			logrus.Debugf("[hijack] End of stdin")
... ...
@@ -238,22 +232,24 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
238 238
 				logrus.Debugf("Couldn't send EOF: %s", err)
239 239
 			}
240 240
 		}
241
-		// Discard errors due to pipe interruption
242
-		return nil
243
-	})
241
+		close(stdinDone)
242
+	}()
244 243
 
245
-	if stdout != nil || stderr != nil {
246
-		if err := <-receiveStdout; err != nil {
244
+	select {
245
+	case err := <-receiveStdout:
246
+		if err != nil {
247 247
 			logrus.Debugf("Error receiveStdout: %s", err)
248
-			return err
249 248
 		}
250
-	}
251
-
252
-	if !cli.isTerminalIn {
253
-		if err := <-sendStdin; err != nil {
254
-			logrus.Debugf("Error sendStdin: %s", err)
255
-			return err
249
+		if cli.isTerminalIn {
250
+			return nil
251
+		}
252
+	case <-stdinDone:
253
+		if stdout != nil || stderr != nil {
254
+			if err := <-receiveStdout; err != nil {
255
+				logrus.Debugf("Error receiveStdout: %s", err)
256
+			}
256 257
 		}
257 258
 	}
259
+
258 260
 	return nil
259 261
 }
... ...
@@ -3384,3 +3384,24 @@ func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) {
3384 3384
 	dockerCmd(c, "stop", "first")
3385 3385
 	dockerCmd(c, "stop", "second")
3386 3386
 }
3387
+
3388
+// #11957 - stdin with no tty does not exit if stdin is not closed even though container exited
3389
+func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) {
3390
+	cmd := exec.Command(dockerBinary, "run", "-i", "--name=test", "busybox", "true")
3391
+	in, err := cmd.StdinPipe()
3392
+	c.Assert(err, check.IsNil)
3393
+	defer in.Close()
3394
+	c.Assert(cmd.Start(), check.IsNil)
3395
+
3396
+	waitChan := make(chan error)
3397
+	go func() {
3398
+		waitChan <- cmd.Wait()
3399
+	}()
3400
+
3401
+	select {
3402
+	case err := <-waitChan:
3403
+		c.Assert(err, check.IsNil)
3404
+	case <-time.After(3 * time.Second):
3405
+		c.Fatal("timeout waiting for command to exit")
3406
+	}
3407
+}