From: Bryan C. Mills Date: Tue, 31 Jan 2023 22:21:14 +0000 (-0500) Subject: [release-branch.go1.20] cmd/go/internal/script: retry ETXTBSY errors in scripts X-Git-Tag: go1.20.1~6 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=00f5d3001a7e684263307ab39c64eba3c79f279c;p=gostls13.git [release-branch.go1.20] cmd/go/internal/script: retry ETXTBSY errors in scripts Fixes #58431. Updates #58019. Change-Id: Ib25d668bfede6e87a3786f44bdc0db1027e3ebec Reviewed-on: https://go-review.googlesource.com/c/go/+/463748 TryBot-Result: Gopher Robot Auto-Submit: Bryan Mills Run-TryBot: Bryan Mills Reviewed-by: Ian Lance Taylor (cherry picked from commit 23c0121e4eb259cc1087d0f79a0803cbc71f500b) Reviewed-on: https://go-review.googlesource.com/c/go/+/466856 Reviewed-by: David Chase --- diff --git a/src/cmd/go/internal/script/cmds.go b/src/cmd/go/internal/script/cmds.go index e0eaad4c43..666d2d62d3 100644 --- a/src/cmd/go/internal/script/cmds.go +++ b/src/cmd/go/internal/script/cmds.go @@ -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) { diff --git a/src/cmd/go/internal/script/cmds_other.go b/src/cmd/go/internal/script/cmds_other.go new file mode 100644 index 0000000000..847b225ae6 --- /dev/null +++ b/src/cmd/go/internal/script/cmds_other.go @@ -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 +} diff --git a/src/cmd/go/internal/script/cmds_posix.go b/src/cmd/go/internal/script/cmds_posix.go new file mode 100644 index 0000000000..2525f6e752 --- /dev/null +++ b/src/cmd/go/internal/script/cmds_posix.go @@ -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) +}