| // Copyright 2024 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. | |
| package race_test | |
| import ( | |
| "runtime" | |
| "sync/atomic" | |
| "testing" | |
| ) | |
| type Seq2[T1, T2 any] func(yield func(T1, T2) bool) | |
| // ofSliceIndex returns a Seq over the elements of s. It is equivalent | |
| // to range s, except that it splits s into two halves and iterates | |
| // in two separate goroutines. This is racy if yield is racy, and yield | |
| // will be racy if it contains an early exit. | |
| func ofSliceIndex[T any, S ~[]T](s S) Seq2[int, T] { | |
| return func(yield func(int, T) bool) { | |
| c := make(chan bool, 2) | |
| var done atomic.Bool | |
| go func() { | |
| for i := 0; i < len(s)/2; i++ { | |
| if !done.Load() && !yield(i, s[i]) { | |
| done.Store(true) | |
| c <- false | |
| } | |
| } | |
| c <- true | |
| }() | |
| go func() { | |
| for i := len(s) / 2; i < len(s); i++ { | |
| if !done.Load() && !yield(i, s[i]) { | |
| done.Store(true) | |
| c <- false | |
| } | |
| } | |
| c <- true | |
| return | |
| }() | |
| if !<-c { | |
| return | |
| } | |
| <-c | |
| } | |
| } | |
| // foo is racy, or not, depending on the value of v | |
| // (0-4 == racy, otherwise, not racy). | |
| func foo(v int) int64 { | |
| var asum atomic.Int64 | |
| for i, x := range ofSliceIndex([]int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) { | |
| if i%5 == v { | |
| break | |
| } | |
| asum.Add(x) // don't race on asum | |
| runtime.Gosched() | |
| } | |
| return 100 + asum.Load() | |
| } | |
| // TestRaceRangeFuncIterator races because x%5 can be equal to 4, | |
| // therefore foo can early exit. | |
| func TestRaceRangeFuncIterator(t *testing.T) { | |
| t.Skip("#72925: uncaught panic ends tests") | |
| x := foo(4) | |
| t.Logf("foo(4)=%d", x) | |
| } | |
| // TestNoRaceRangeFuncIterator does not race because x%5 is never 5, | |
| // therefore foo's loop will not exit early, and this it will not race. | |
| func TestNoRaceRangeFuncIterator(t *testing.T) { | |
| t.Skip("#72925: unexpected data race") | |
| x := foo(5) | |
| t.Logf("foo(5)=%d", x) | |
| } | |