c.Stdin = pr
c.closeAfterStart = append(c.closeAfterStart, pr)
wc := &closeOnce{File: pw}
- c.closeAfterWait = append(c.closeAfterWait, closerFunc(wc.safeClose))
+ c.closeAfterWait = append(c.closeAfterWait, wc)
return wc, nil
}
type closeOnce struct {
*os.File
- writers sync.RWMutex // coordinate safeClose and Write
- once sync.Once
- err error
+ once sync.Once
+ err error
}
func (c *closeOnce) Close() error {
c.err = c.File.Close()
}
-type closerFunc func() error
-
-func (f closerFunc) Close() error { return f() }
-
-// safeClose closes c being careful not to race with any calls to c.Write.
-// See golang.org/issue/9307 and TestEchoFileRace in exec_test.go.
-// In theory other calls could also be excluded (by writing appropriate
-// wrappers like c.Write's implementation below), but since c is most
-// commonly used as a WriteCloser, Write is the main one to worry about.
-// See also #7970, for which this is a partial fix for this specific instance.
-// The idea is that we return a WriteCloser, and so the caller can be
-// relied upon not to call Write and Close simultaneously, but it's less
-// obvious that cmd.Wait calls Close and that the caller must not call
-// Write and cmd.Wait simultaneously. In fact that seems too onerous.
-// So we change the use of Close in cmd.Wait to use safeClose, which will
-// synchronize with any Write.
-//
-// It's important that we know this won't block forever waiting for the
-// operations being excluded. At the point where this is called,
-// the invoked command has exited and the parent copy of the read side
-// of the pipe has also been closed, so there should really be no read side
-// of the pipe left. Any active writes should return very shortly with an EPIPE,
-// making it reasonable to wait for them.
-// Technically it is possible that the child forked a sub-process or otherwise
-// handed off the read side of the pipe before exiting and the current holder
-// is not reading from the pipe, and the pipe is full, in which case the close here
-// might block waiting for the write to complete. That's probably OK.
-// It's a small enough problem to be outweighed by eliminating the race here.
-func (c *closeOnce) safeClose() error {
- c.writers.Lock()
- err := c.Close()
- c.writers.Unlock()
- return err
-}
-
-func (c *closeOnce) Write(b []byte) (int, error) {
- c.writers.RLock()
- n, err := c.File.Write(b)
- c.writers.RUnlock()
- return n, err
-}
-
-func (c *closeOnce) WriteString(s string) (int, error) {
- c.writers.RLock()
- n, err := c.File.WriteString(s)
- c.writers.RUnlock()
- return n, err
-}
-
// StdoutPipe returns a pipe that will be connected to the command's
// standard output when the command starts.
//