aboutsummaryrefslogtreecommitdiff
path: root/src/os/exec/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/os/exec/exec.go')
-rw-r--r--src/os/exec/exec.go8
1 files changed, 7 insertions, 1 deletions
diff --git a/src/os/exec/exec.go b/src/os/exec/exec.go
index e84ebfc453..e0c509cbef 100644
--- a/src/os/exec/exec.go
+++ b/src/os/exec/exec.go
@@ -102,6 +102,7 @@ import (
"runtime"
"strconv"
"strings"
+ "sync/atomic"
"syscall"
"time"
)
@@ -354,6 +355,9 @@ type Cmd struct {
// the work of resolving the extension, so Start doesn't need to do it again.
// This is only used on Windows.
cachedLookExtensions struct{ in, out string }
+
+ // startCalled records that Start was attempted, regardless of outcome.
+ startCalled atomic.Bool
}
// A ctxResult reports the result of watching the Context associated with a
@@ -635,7 +639,8 @@ func (c *Cmd) Run() error {
func (c *Cmd) Start() error {
// Check for doubled Start calls before we defer failure cleanup. If the prior
// call to Start succeeded, we don't want to spuriously close its pipes.
- if c.Process != nil {
+ // It is an error to call Start twice even if the first call did not create a process.
+ if c.startCalled.Swap(true) {
return errors.New("exec: already started")
}
@@ -647,6 +652,7 @@ func (c *Cmd) Start() error {
if !started {
closeDescriptors(c.parentIOPipes)
c.parentIOPipes = nil
+ c.goroutine = nil // aid GC, finalization of pipe fds
}
}()