Created subprocess.Cmd wrapper to autoclose any created pipes
Removing original fix for #1860
This commit is contained in:
Taylor Jones 2017-01-17 18:41:38 -05:00 committed by Taylor Jones
parent dd19c1b9c2
commit be73bc08dd
5 changed files with 54 additions and 11 deletions

@ -445,10 +445,7 @@ func RecentBranches(since time.Time, includeRemoteBranches bool, onlyRemote stri
return nil, fmt.Errorf("Failed to call git for-each-ref: %v", err) return nil, fmt.Errorf("Failed to call git for-each-ref: %v", err)
} }
cmd.Start() cmd.Start()
defer func() { defer cmd.Wait()
outp.Close()
cmd.Wait()
}()
scanner := bufio.NewScanner(outp) scanner := bufio.NewScanner(outp)

46
subprocess/cmd.go Normal file

@ -0,0 +1,46 @@
package subprocess
import (
"io"
"os/exec"
)
// Thin wrapper around exec.Cmd. Takes care of pipe shutdown by
// keeping an internal reference to any created pipes. Whenever
// Cmd.Wait() is called, all created pipes are closed.
type Cmd struct {
*exec.Cmd
pipes []io.Closer
}
func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
stdout, err := c.Cmd.StdoutPipe()
c.pipes = append(c.pipes, stdout)
return stdout, err
}
func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
stderr, err := c.Cmd.StderrPipe()
c.pipes = append(c.pipes, stderr)
return stderr, err
}
func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
stdin, err := c.Cmd.StdinPipe()
c.pipes = append(c.pipes, stdin)
return stdin, err
}
func (c *Cmd) Wait() error {
for _, pipe := range c.pipes {
pipe.Close()
}
return c.Cmd.Wait()
}
func newCmd(cmd *exec.Cmd) *Cmd {
wrapped := &Cmd{Cmd: cmd}
return wrapped
}

@ -7,8 +7,8 @@ import (
) )
// ExecCommand is a small platform specific wrapper around os/exec.Command // ExecCommand is a small platform specific wrapper around os/exec.Command
func ExecCommand(name string, arg ...string) *exec.Cmd { func ExecCommand(name string, arg ...string) *Cmd {
cmd := exec.Command(name, arg...) cmd := exec.Command(name, arg...)
cmd.Env = env cmd.Env = env
return cmd return newCmd(cmd)
} }

@ -8,9 +8,9 @@ import (
) )
// ExecCommand is a small platform specific wrapper around os/exec.Command // ExecCommand is a small platform specific wrapper around os/exec.Command
func ExecCommand(name string, arg ...string) *exec.Cmd { func ExecCommand(name string, arg ...string) *Cmd {
cmd := exec.Command(name, arg...) cmd := exec.Command(name, arg...)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Env = env cmd.Env = env
return cmd return newCmd(cmd)
} }

@ -6,7 +6,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
@ -52,7 +51,7 @@ func (t *traceWriter) Flush() {
type customAdapterWorkerContext struct { type customAdapterWorkerContext struct {
workerNum int workerNum int
cmd *exec.Cmd cmd *subprocess.Cmd
stdout io.ReadCloser stdout io.ReadCloser
bufferedOut *bufio.Reader bufferedOut *bufio.Reader
stdin io.WriteCloser stdin io.WriteCloser
@ -70,7 +69,8 @@ func NewCustomAdapterInitRequest(op string, concurrent bool, concurrentTransfers
return &customAdapterInitRequest{"init", op, concurrent, concurrentTransfers} return &customAdapterInitRequest{"init", op, concurrent, concurrentTransfers}
} }
type customAdapterTransferRequest struct { // common between upload/download type customAdapterTransferRequest struct {
// common between upload/download
Event string `json:"event"` Event string `json:"event"`
Oid string `json:"oid"` Oid string `json:"oid"`
Size int64 `json:"size"` Size int64 `json:"size"`