浏览代码

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

Solomon Hykes 12 年之前
父节点
当前提交
fdae64d8d7
共有 2 个文件被更改,包括 23 次插入5 次删除
  1. 9 5
      archive.go
  2. 14 0
      archive_test.go

+ 9 - 5
archive.go

@@ -55,17 +55,21 @@ func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
 		return nil, err
 	}
 	pipeR, pipeW := io.Pipe()
+	errChan := make(chan []byte)
 	go func() {
-		_, err := io.Copy(pipeW, stdout)
-		if err != nil {
-			pipeW.CloseWithError(err)
-		}
 		errText, e := ioutil.ReadAll(stderr)
 		if e != nil {
 			errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
 		}
+		errChan <- errText
+	}()
+	go func() {
+		_, err := io.Copy(pipeW, stdout)
+		if err != nil {
+			pipeW.CloseWithError(err)
+		}
+		errText := <-errChan
 		if err := cmd.Wait(); err != nil {
-			// FIXME: can this block if stderr outputs more than the size of StderrPipe()'s buffer?
 			pipeW.CloseWithError(errors.New(err.Error() + ": " + string(errText)))
 		} else {
 			pipeW.Close()

+ 14 - 0
archive_test.go

@@ -1,12 +1,26 @@
 package docker
 
 import (
+	"io"
 	"io/ioutil"
 	"os"
 	"os/exec"
 	"testing"
 )
 
+func TestCmdStreamLargeStderr(t *testing.T) {
+	// This test checks for deadlock; thus, the main failure mode of this test is deadlocking.
+	cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
+	out, err := CmdStream(cmd)
+	if err != nil {
+		t.Fatalf("Failed to start command: " + err.Error())
+	}
+	_, err = io.Copy(ioutil.Discard, out)
+	if err != nil {
+		t.Fatalf("Command should not have failed (err=%s...)", err.Error()[:100])
+	}
+}
+
 func TestCmdStreamBad(t *testing.T) {
 	badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
 	out, err := CmdStream(badCmd)