Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix a race detected only if a test times out #3808

Merged
merged 2 commits into from
Jan 2, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix a race detected only if a test times out
I believe this can happen because the goroutine is started before the
command is started. Start the process first, then on success, start the
signal handler. Add a test for regression.

I verified the test fails without my change to wrap.go and the race is
from the signal handling goroutine.
  • Loading branch information
patrickmscott committed Jan 2, 2024
commit 31b1b9aaa01f3fd9a381df1b941926827ea0b518
27 changes: 15 additions & 12 deletions go/tools/bzltestutil/wrap.go
Original file line number Diff line number Diff line change
@@ -128,19 +128,22 @@ func Wrap(pkg string) error {
cmd.Env = append(os.Environ(), "GO_TEST_WRAP=0")
cmd.Stderr = io.MultiWriter(os.Stderr, streamMerger.ErrW)
cmd.Stdout = io.MultiWriter(os.Stdout, streamMerger.OutW)
go func() {
// When the Bazel test timeout is reached, Bazel sends a SIGTERM that
// we need to forward to the inner process.
// TODO: This never triggers on Windows, even though Go should simulate
// a SIGTERM when Windows asks the process to close. It is not clear
// whether Bazel uses the required graceful shutdown mechanism.
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGTERM)
<-c
cmd.Process.Signal(syscall.SIGTERM)
}()
streamMerger.Start()
err := cmd.Run()
err := cmd.Start()
if err == nil {
go func() {
// When the Bazel test timeout is reached, Bazel sends a SIGTERM that
// we need to forward to the inner process.
// TODO: This never triggers on Windows, even though Go should simulate
// a SIGTERM when Windows asks the process to close. It is not clear
// whether Bazel uses the required graceful shutdown mechanism.
c := make(chan os.Signal, 1)
fmeum marked this conversation as resolved.
Show resolved Hide resolved
signal.Notify(c, syscall.SIGTERM)
<-c
cmd.Process.Signal(syscall.SIGTERM)
}()
err = cmd.Wait()
}
streamMerger.ErrW.Close()
streamMerger.OutW.Close()
streamMerger.Wait()
37 changes: 37 additions & 0 deletions tests/core/race/race_test.go
Original file line number Diff line number Diff line change
@@ -94,6 +94,12 @@ go_test(
embed = [":coverrace"],
race = "on",
)

go_test(
name = "timeout_test",
srcs = ["timeout_test.go"],
race = "on",
)
-- race_off.go --
// +build !race

@@ -193,10 +199,41 @@ func TestCoverRace(t *testing.T) {
t.Errorf("got %d, want %d", got, 100)
}
}

-- timeout_test.go --
package main

import (
"testing"
"time"
)

func TestTimeout(t *testing.T) {
time.Sleep(10*time.Second)
}
`,
})
}

func TestTimeout(t *testing.T) {
cmd := bazel_testing.BazelCmd("test", "//:timeout_test", "--test_timeout=1", "--test_output=all")
stdout := &bytes.Buffer{}
cmd.Stdout = stdout
t.Logf("running: %s", strings.Join(cmd.Args, " "))
err := cmd.Run()
t.Log(stdout.String())
if err == nil {
t.Fatalf("expected bazel test to fail")
}
var xerr *exec.ExitError
if !errors.As(err, &xerr) || xerr.ExitCode() != bazel_testing.TESTS_FAILED {
t.Fatalf("unexpected error: %v", err)
}
if bytes.Contains(stdout.Bytes(), []byte("WARNING: DATA RACE")) {
t.Fatalf("unexpected data race; command failed with: %v\nstdout:\n%s", err, stdout.Bytes())
}
}

func Test(t *testing.T) {
for _, test := range []struct {
desc, cmd, target string