The following test should reliably fail when run with -race, because it has multiple goroutines (via t.Parallel()) that all read from the (unsynchronized, changing) loop variable.
https://play.golang.org/p/nHo8MaTpS3o
func TestParallelRace(t *testing.T) {
testCases := []struct {
i int
}{
{0},
{1},
{2},
}
for _, tc := range testCases {
t.Run(fmt.Sprint(tc.i), func(t *testing.T) {
t.Parallel()
t.Log(tc.i)
})
}
}
However, because t.Parallel blocks the goroutine until the main test function returns, the race goes undetected: as far as the race detector is concerned, the program unambiguously meant to use only the last value of the loop variable.
This unintended synchronization masks real bugs, such as the one reported in #35632.
#16520 requests a vet check that would detect this and similar cases, but perhaps there is something we can do within the implementation of t.Parallel in order to expose these races too.
$ go version
go version devel +2bde3c13 Mon Nov 18 05:26:46 2019 +0000 linux/amd64
CC @ianthehat @matloob @randall77
The following test should reliably fail when run with
-race, because it has multiple goroutines (viat.Parallel()) that all read from the (unsynchronized, changing) loop variable.https://play.golang.org/p/nHo8MaTpS3o
However, because
t.Parallelblocks the goroutine until the main test function returns, the race goes undetected: as far as the race detector is concerned, the program unambiguously meant to use only the last value of the loop variable.This unintended synchronization masks real bugs, such as the one reported in #35632.
#16520 requests a
vetcheck that would detect this and similar cases, but perhaps there is something we can do within the implementation oft.Parallelin order to expose these races too.CC @ianthehat @matloob @randall77