diff options
| author | Bryan C. Mills <bcmills@google.com> | 2022-04-22 21:19:10 -0400 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2022-09-26 17:26:59 +0000 |
| commit | bd8a5b00fcf71fb711ba8996a880b5b07f7b3634 (patch) | |
| tree | cad512abd760b40affd7a7dc934ca0e659c74568 /src/os/exec | |
| parent | b8d4a14a660827ea0331eb6cad99860bf5fdf66f (diff) | |
| download | go-bd8a5b00fcf71fb711ba8996a880b5b07f7b3634.tar.xz | |
os/exec: split parent I/O pipes by whether they are pumped by user code or internal goroutines
The pipes pumped by goroutines can be closed as soon as their
respective goroutines are done.
The pipes pumped by user code, however, are documented to be closed in
Wait. When we add the WaitDelay field, it isn't obvious that we should
terminate the user-pumped pipes when the WaitDelay expires, since Wait
itself isn't going to wait for those user-controlled goroutines to
complete.
(It's a bit more complicated than that because the documentation
currently states that Wait must not be called while the pipes are
being read — but it isn't obvious to me that that advice is entirely
correct.)
For #50436.
Change-Id: I97909c91d2097fb75138a360747168c08609696d
Reviewed-on: https://go-review.googlesource.com/c/go/+/401894
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Diffstat (limited to 'src/os/exec')
| -rw-r--r-- | src/os/exec/exec.go | 37 |
1 files changed, 23 insertions, 14 deletions
diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go index 9bef38533e..67dd379b71 100644 --- a/src/os/exec/exec.go +++ b/src/os/exec/exec.go @@ -226,11 +226,17 @@ type Cmd struct { // are inherited by the child process. childIOFiles []io.Closer - // parentIOPipes holds closers for the parent's end of any pipes + // goroutinePipes holds closers for the parent's end of any pipes // connected to the child's stdin, stdout, and/or stderr streams - // that were opened by the Cmd itself (not supplied by the caller). - // These should be closed after Wait sees the command exit. - parentIOPipes []io.Closer + // that are pumped (and ultimately closed) by goroutines controlled by + // the Cmd itself (not supplied by or returned to the caller). + goroutinePipes []io.Closer + + // userPipes holds closers for the parent's end of any pipes + // connected to the child's stdin, stdout and/or stderr streams + // that were opened and returned by the Cmd's Pipe methods. + // These should be closed when Wait completes. + userPipes []io.Closer goroutine []func() error goroutineErrs <-chan error // one receive per goroutine @@ -367,7 +373,7 @@ func (c *Cmd) childStdin() (*os.File, error) { } c.childIOFiles = append(c.childIOFiles, pr) - c.parentIOPipes = append(c.parentIOPipes, pw) + c.goroutinePipes = append(c.goroutinePipes, pw) c.goroutine = append(c.goroutine, func() error { _, err := io.Copy(pw, c.Stdin) if skipStdinCopyError(err) { @@ -416,7 +422,7 @@ func (c *Cmd) writerDescriptor(w io.Writer) (*os.File, error) { } c.childIOFiles = append(c.childIOFiles, pw) - c.parentIOPipes = append(c.parentIOPipes, pr) + c.goroutinePipes = append(c.goroutinePipes, pr) c.goroutine = append(c.goroutine, func() error { _, err := io.Copy(w, pr) pr.Close() // in case io.Copy stopped due to write error @@ -490,8 +496,10 @@ func (c *Cmd) Start() error { c.childIOFiles = nil if !started { - c.closeDescriptors(c.parentIOPipes) - c.parentIOPipes = nil + c.closeDescriptors(c.goroutinePipes) + c.goroutinePipes = nil + c.closeDescriptors(c.userPipes) + c.userPipes = nil } }() @@ -630,7 +638,8 @@ func (c *Cmd) Wait() error { copyError = err } } - c.goroutine = nil // Allow the goroutines' closures to be GC'd. + c.goroutine = nil // Allow the goroutines' closures to be GC'd. + c.goroutinePipes = nil // Already closed by their respective goroutines. if c.ctxErr != nil { interruptErr := <-c.ctxErr @@ -647,8 +656,8 @@ func (c *Cmd) Wait() error { err = copyError } - c.closeDescriptors(c.parentIOPipes) - c.parentIOPipes = nil + c.closeDescriptors(c.userPipes) + c.userPipes = nil return err } @@ -749,7 +758,7 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, error) { c.Stdin = pr c.childIOFiles = append(c.childIOFiles, pr) wc := &closeOnce{File: pw} - c.parentIOPipes = append(c.parentIOPipes, wc) + c.userPipes = append(c.userPipes, wc) return wc, nil } @@ -790,7 +799,7 @@ func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { } c.Stdout = pw c.childIOFiles = append(c.childIOFiles, pw) - c.parentIOPipes = append(c.parentIOPipes, pr) + c.userPipes = append(c.userPipes, pr) return pr, nil } @@ -815,7 +824,7 @@ func (c *Cmd) StderrPipe() (io.ReadCloser, error) { } c.Stderr = pw c.childIOFiles = append(c.childIOFiles, pw) - c.parentIOPipes = append(c.parentIOPipes, pr) + c.userPipes = append(c.userPipes, pr) return pr, nil } |
