Skip to content

testing: t.Parallel in a subtest masks races with the outer test function #35670

@bcmills

Description

@bcmills

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions