Browse code

Merge remote-tracking branch 'robryk/cmdstream-deadlock'

Solomon Hykes authored on 2013/03/30 05:20:17
Showing 2 changed files
... ...
@@ -55,17 +55,21 @@ func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
55 55
 		return nil, err
56 56
 	}
57 57
 	pipeR, pipeW := io.Pipe()
58
+	errChan := make(chan []byte)
58 59
 	go func() {
59
-		_, err := io.Copy(pipeW, stdout)
60
-		if err != nil {
61
-			pipeW.CloseWithError(err)
62
-		}
63 60
 		errText, e := ioutil.ReadAll(stderr)
64 61
 		if e != nil {
65 62
 			errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
66 63
 		}
64
+		errChan <- errText
65
+	}()
66
+	go func() {
67
+		_, err := io.Copy(pipeW, stdout)
68
+		if err != nil {
69
+			pipeW.CloseWithError(err)
70
+		}
71
+		errText := <-errChan
67 72
 		if err := cmd.Wait(); err != nil {
68
-			// FIXME: can this block if stderr outputs more than the size of StderrPipe()'s buffer?
69 73
 			pipeW.CloseWithError(errors.New(err.Error() + ": " + string(errText)))
70 74
 		} else {
71 75
 			pipeW.Close()
... ...
@@ -1,12 +1,26 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"io"
4 5
 	"io/ioutil"
5 6
 	"os"
6 7
 	"os/exec"
7 8
 	"testing"
8 9
 )
9 10
 
11
+func TestCmdStreamLargeStderr(t *testing.T) {
12
+	// This test checks for deadlock; thus, the main failure mode of this test is deadlocking.
13
+	cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
14
+	out, err := CmdStream(cmd)
15
+	if err != nil {
16
+		t.Fatalf("Failed to start command: " + err.Error())
17
+	}
18
+	_, err = io.Copy(ioutil.Discard, out)
19
+	if err != nil {
20
+		t.Fatalf("Command should not have failed (err=%s...)", err.Error()[:100])
21
+	}
22
+}
23
+
10 24
 func TestCmdStreamBad(t *testing.T) {
11 25
 	badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
12 26
 	out, err := CmdStream(badCmd)