cmd/go/internal/script: retry ETXTBSY errors in scripts

Fixes #58019.

Change-Id: Ib25d668bfede6e87a3786f44bdc0db1027e3ebec
Reviewed-on: https://go-review.googlesource.com/c/go/+/463748
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Bryan C. Mills 2023-01-31 17:21:14 -05:00 committed by Gopher Robot
parent 356647409d
commit 23c0121e4e
3 changed files with 58 additions and 15 deletions

View File

@ -432,21 +432,37 @@ func Exec(cancel func(*exec.Cmd) error, waitDelay time.Duration) Cmd {
}
func startCommand(s *State, name, path string, args []string, cancel func(*exec.Cmd) error, waitDelay time.Duration) (WaitFunc, error) {
var stdoutBuf, stderrBuf strings.Builder
cmd := exec.CommandContext(s.Context(), path, args...)
if cancel == nil {
cmd.Cancel = nil
} else {
cmd.Cancel = func() error { return cancel(cmd) }
}
cmd.WaitDelay = waitDelay
cmd.Args[0] = name
cmd.Dir = s.Getwd()
cmd.Env = s.env
cmd.Stdout = &stdoutBuf
cmd.Stderr = &stderrBuf
if err := cmd.Start(); err != nil {
return nil, err
var (
cmd *exec.Cmd
stdoutBuf, stderrBuf strings.Builder
)
for {
cmd = exec.CommandContext(s.Context(), path, args...)
if cancel == nil {
cmd.Cancel = nil
} else {
cmd.Cancel = func() error { return cancel(cmd) }
}
cmd.WaitDelay = waitDelay
cmd.Args[0] = name
cmd.Dir = s.Getwd()
cmd.Env = s.env
cmd.Stdout = &stdoutBuf
cmd.Stderr = &stderrBuf
err := cmd.Start()
if err == nil {
break
}
if isETXTBSY(err) {
// If the script (or its host process) just wrote the executable we're
// trying to run, a fork+exec in another thread may be holding open the FD
// that we used to write the executable (see https://go.dev/issue/22315).
// Since the descriptor should have CLOEXEC set, the problem should
// resolve as soon as the forked child reaches its exec call.
// Keep retrying until that happens.
} else {
return nil, err
}
}
wait := func(s *State) (stdout, stderr string, err error) {

View File

@ -0,0 +1,11 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !(unix || windows)
package script
func isETXTBSY(err error) bool {
return false
}

View File

@ -0,0 +1,16 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build unix || windows
package script
import (
"errors"
"syscall"
)
func isETXTBSY(err error) bool {
return errors.Is(err, syscall.ETXTBSY)
}