This is an offshoot of #74327.

The following program contains a data race as reported by the race detector:

func Test(t *testing.T) {
        synctest.Test(t, func(t *testing.T) {
                var v int
                go func() {
                        time.Sleep(1)
                        v = 0 // data race: write
                }()
                _ = v // data race: read
                time.Sleep(2)
        })
}

It should be obvious that the access to v is racy when executed outside of a synctest bubble: The time.Sleep(1) call does not establish a happens-before relationship between the read and the write. Within the fake time environment of a synctest bubble, the time.Sleep(1) will not return until after the read has occurred. However, we deliberately do not tell the race detector to establish a happens-before relationship here, to allow it to detect racing operations in a system under test that might be obscured by the use of fake time.

The synctest.Wait function establishes a happens-before relationship between all activity in a bubble and the Wait call returning. Therefore, this program does not contain a data race:

func Test(t *testing.T) {
        synctest.Test(t, func(t *testing.T) {
                var v int
                go func() {
                        time.Sleep(1)
                        v = 0
                }()
                time.Sleep(2)
                synctest.Wait() // v = 0 happens-before Wait returns
                _ = v
        })
}

However, perhaps surprisingly, the race detector does report a race in this program:

func Test(t *testing.T) {
        synctest.Test(t, func(t *testing.T) {
                var v int
                go func() {
                        time.Sleep(1)
                        v = 0
                }()
                synctest.Wait()
                _ = v
                synctest.Wait()
                time.Sleep(2)
        })
}

The user's intent in this test seems clear, but nothing in this program establishes a happens-before relationship between the two accesses to v.

Perhaps there's some tweak we can make to the happens-before relationships established by Wait that would allow us to avoid reporting a data race in this case. I'm not sure exactly what that would be.

We do want to take care not to make a change that masks data races in goroutines which do not call Wait, such as in this example:

func Test(t *testing.T) {
        synctest.Test(t, func(t *testing.T) {
                var v int
                go func() {
                        time.Sleep(1)
                        v = 0
                }()
                go func() {
                        time.Sleep(2)
                        _ = v
                }()
                // None of these Waits should establish a happens-before relationship
                // between the two goroutines above.
                synctest.Wait()
                time.Sleep(1)
                synctest.Wait()
                time.Sleep(1) 
        })
}

Comment From: gabyhelp

Related Issues

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)