aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/race/testdata/rangefunc_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/race/testdata/rangefunc_test.go')
-rw-r--r--src/runtime/race/testdata/rangefunc_test.go77
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)
+}