mirror of
https://github.com/golang/go
synced 2024-11-23 04:40:09 -07:00
cmd/go: retry ETXTBSY errors when running test binaries
An ETXTBSY error when starting a test binary is almost certainly caused by the race reported in #22315. That race will resolve quickly on its own, so we should just retry the command instead of reporting a spurious failure. Fixes #62221. Change-Id: I408f3eaa7ab5d7efbc7a2b1c8bea3dbc459fc794 Reviewed-on: https://go-review.googlesource.com/c/go/+/522015 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Bryan Mills <bcmills@google.com> Auto-Submit: Bryan Mills <bcmills@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
parent
52e17c2e4d
commit
4dc2564933
@ -1368,65 +1368,87 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action)
|
||||
ctx, cancel := context.WithTimeout(ctx, testKillTimeout)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
|
||||
cmd.Dir = a.Package.Dir
|
||||
|
||||
env := slices.Clip(cfg.OrigEnv)
|
||||
env = base.AppendPATH(env)
|
||||
env = base.AppendPWD(env, cmd.Dir)
|
||||
cmd.Env = env
|
||||
if addToEnv != "" {
|
||||
cmd.Env = append(cmd.Env, addToEnv)
|
||||
}
|
||||
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stdout
|
||||
|
||||
// If there are any local SWIG dependencies, we want to load
|
||||
// the shared library from the build directory.
|
||||
if a.Package.UsesSwig() {
|
||||
env := cmd.Env
|
||||
found := false
|
||||
prefix := "LD_LIBRARY_PATH="
|
||||
for i, v := range env {
|
||||
if strings.HasPrefix(v, prefix) {
|
||||
env[i] = v + ":."
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
env = append(env, "LD_LIBRARY_PATH=.")
|
||||
}
|
||||
cmd.Env = env
|
||||
}
|
||||
// Now we're ready to actually run the command.
|
||||
//
|
||||
// If the -o flag is set, or if at some point we change cmd/go to start
|
||||
// copying test executables into the build cache, we may run into spurious
|
||||
// ETXTBSY errors on Unix platforms (see https://go.dev/issue/22315).
|
||||
//
|
||||
// Since we know what causes those, and we know that they should resolve
|
||||
// quickly (the ETXTBSY error will resolve as soon as the subprocess
|
||||
// holding the descriptor open reaches its 'exec' call), we retry them
|
||||
// in a loop.
|
||||
|
||||
var (
|
||||
cmd *exec.Cmd
|
||||
t0 time.Time
|
||||
cancelKilled = false
|
||||
cancelSignaled = false
|
||||
)
|
||||
cmd.Cancel = func() error {
|
||||
if base.SignalTrace == nil {
|
||||
err := cmd.Process.Kill()
|
||||
for {
|
||||
cmd = exec.CommandContext(ctx, args[0], args[1:]...)
|
||||
cmd.Dir = a.Package.Dir
|
||||
|
||||
env := slices.Clip(cfg.OrigEnv)
|
||||
env = base.AppendPATH(env)
|
||||
env = base.AppendPWD(env, cmd.Dir)
|
||||
cmd.Env = env
|
||||
if addToEnv != "" {
|
||||
cmd.Env = append(cmd.Env, addToEnv)
|
||||
}
|
||||
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stdout
|
||||
|
||||
// If there are any local SWIG dependencies, we want to load
|
||||
// the shared library from the build directory.
|
||||
if a.Package.UsesSwig() {
|
||||
env := cmd.Env
|
||||
found := false
|
||||
prefix := "LD_LIBRARY_PATH="
|
||||
for i, v := range env {
|
||||
if strings.HasPrefix(v, prefix) {
|
||||
env[i] = v + ":."
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
env = append(env, "LD_LIBRARY_PATH=.")
|
||||
}
|
||||
cmd.Env = env
|
||||
}
|
||||
|
||||
cmd.Cancel = func() error {
|
||||
if base.SignalTrace == nil {
|
||||
err := cmd.Process.Kill()
|
||||
if err == nil {
|
||||
cancelKilled = true
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Send a quit signal in the hope that the program will print
|
||||
// a stack trace and exit.
|
||||
err := cmd.Process.Signal(base.SignalTrace)
|
||||
if err == nil {
|
||||
cancelKilled = true
|
||||
cancelSignaled = true
|
||||
}
|
||||
return err
|
||||
}
|
||||
cmd.WaitDelay = testWaitDelay
|
||||
|
||||
// Send a quit signal in the hope that the program will print
|
||||
// a stack trace and exit.
|
||||
err := cmd.Process.Signal(base.SignalTrace)
|
||||
if err == nil {
|
||||
cancelSignaled = true
|
||||
base.StartSigHandlers()
|
||||
t0 = time.Now()
|
||||
err = cmd.Run()
|
||||
|
||||
if !isETXTBSY(err) {
|
||||
// We didn't hit the race in #22315, so there is no reason to retry the
|
||||
// command.
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
cmd.WaitDelay = testWaitDelay
|
||||
|
||||
base.StartSigHandlers()
|
||||
t0 := time.Now()
|
||||
err = cmd.Run()
|
||||
out := buf.Bytes()
|
||||
a.TestOutput = &buf
|
||||
t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds())
|
||||
|
12
src/cmd/go/internal/test/test_nonunix.go
Normal file
12
src/cmd/go/internal/test/test_nonunix.go
Normal file
@ -0,0 +1,12 @@
|
||||
// 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
|
||||
|
||||
package test
|
||||
|
||||
func isETXTBSY(err error) bool {
|
||||
// syscall.ETXTBSY is only meaningful on Unix platforms.
|
||||
return false
|
||||
}
|
16
src/cmd/go/internal/test/test_unix.go
Normal file
16
src/cmd/go/internal/test/test_unix.go
Normal 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
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func isETXTBSY(err error) bool {
|
||||
return errors.Is(err, syscall.ETXTBSY)
|
||||
}
|
@ -2,6 +2,10 @@
|
||||
|
||||
# Verify test -c can output multiple executables to a directory.
|
||||
|
||||
# This test also serves as a regression test for https://go.dev/issue/62221:
|
||||
# prior to the fix for that issue, it occasionally failed with ETXTBSY when
|
||||
# run on Unix platforms.
|
||||
|
||||
go test -c -o $WORK/some/nonexisting/directory/ ./pkg/...
|
||||
exists -exec $WORK/some/nonexisting/directory/pkg1.test$GOEXE
|
||||
exists -exec $WORK/some/nonexisting/directory/pkg2.test$GOEXE
|
||||
@ -43,4 +47,4 @@ package pkg1
|
||||
package pkg2
|
||||
|
||||
-- anotherpkg/pkg1/pkg1_test.go --
|
||||
package pkg1
|
||||
package pkg1
|
||||
|
Loading…
Reference in New Issue
Block a user