Skip to content

Commit 00f5d30

Browse files
Bryan C. Millsdr2chase
Bryan C. Mills
authored andcommitted
[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 <[email protected]> Auto-Submit: Bryan Mills <[email protected]> Run-TryBot: Bryan Mills <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> (cherry picked from commit 23c0121) Reviewed-on: https://go-review.googlesource.com/c/go/+/466856 Reviewed-by: David Chase <[email protected]>
1 parent 7628627 commit 00f5d30

File tree

3 files changed

+58
-15
lines changed

3 files changed

+58
-15
lines changed

src/cmd/go/internal/script/cmds.go

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -432,21 +432,37 @@ func Exec(cancel func(*exec.Cmd) error, waitDelay time.Duration) Cmd {
432432
}
433433

434434
func startCommand(s *State, name, path string, args []string, cancel func(*exec.Cmd) error, waitDelay time.Duration) (WaitFunc, error) {
435-
var stdoutBuf, stderrBuf strings.Builder
436-
cmd := exec.CommandContext(s.Context(), path, args...)
437-
if cancel == nil {
438-
cmd.Cancel = nil
439-
} else {
440-
cmd.Cancel = func() error { return cancel(cmd) }
441-
}
442-
cmd.WaitDelay = waitDelay
443-
cmd.Args[0] = name
444-
cmd.Dir = s.Getwd()
445-
cmd.Env = s.env
446-
cmd.Stdout = &stdoutBuf
447-
cmd.Stderr = &stderrBuf
448-
if err := cmd.Start(); err != nil {
449-
return nil, err
435+
var (
436+
cmd *exec.Cmd
437+
stdoutBuf, stderrBuf strings.Builder
438+
)
439+
for {
440+
cmd = exec.CommandContext(s.Context(), path, args...)
441+
if cancel == nil {
442+
cmd.Cancel = nil
443+
} else {
444+
cmd.Cancel = func() error { return cancel(cmd) }
445+
}
446+
cmd.WaitDelay = waitDelay
447+
cmd.Args[0] = name
448+
cmd.Dir = s.Getwd()
449+
cmd.Env = s.env
450+
cmd.Stdout = &stdoutBuf
451+
cmd.Stderr = &stderrBuf
452+
err := cmd.Start()
453+
if err == nil {
454+
break
455+
}
456+
if isETXTBSY(err) {
457+
// If the script (or its host process) just wrote the executable we're
458+
// trying to run, a fork+exec in another thread may be holding open the FD
459+
// that we used to write the executable (see https://go.dev/issue/22315).
460+
// Since the descriptor should have CLOEXEC set, the problem should
461+
// resolve as soon as the forked child reaches its exec call.
462+
// Keep retrying until that happens.
463+
} else {
464+
return nil, err
465+
}
450466
}
451467

452468
wait := func(s *State) (stdout, stderr string, err error) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build !(unix || windows)
6+
7+
package script
8+
9+
func isETXTBSY(err error) bool {
10+
return false
11+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build unix || windows
6+
7+
package script
8+
9+
import (
10+
"errors"
11+
"syscall"
12+
)
13+
14+
func isETXTBSY(err error) bool {
15+
return errors.Is(err, syscall.ETXTBSY)
16+
}

0 commit comments

Comments
 (0)