diff options
Diffstat (limited to 'src/runtime/race/testdata/rangefunc_test.go')
| -rw-r--r-- | src/runtime/race/testdata/rangefunc_test.go | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/runtime/race/testdata/rangefunc_test.go b/src/runtime/race/testdata/rangefunc_test.go new file mode 100644 index 0000000000..f2ff793df7 --- /dev/null +++ b/src/runtime/race/testdata/rangefunc_test.go @@ -0,0 +1,77 @@ +// 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. + +//go:build goexperiment.rangefunc + +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) { + 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) { + x := foo(5) + t.Logf("foo(5)=%d", x) +} |
