diff options
| author | David Chase <drchase@google.com> | 2024-01-26 17:49:33 -0500 |
|---|---|---|
| committer | David Chase <drchase@google.com> | 2024-05-21 21:08:03 +0000 |
| commit | 5a9dabc2ba873f89a5de9ff53c8eab7c96d0c6b5 (patch) | |
| tree | 0012a70f6ab947152eb5702dc102c4bd6275252c /src/runtime/race/testdata | |
| parent | 643865856c535a5a644201c602b18bd87b121c93 (diff) | |
| download | go-5a9dabc2ba873f89a5de9ff53c8eab7c96d0c6b5.tar.xz | |
cmd/compile: for rangefunc, add checks and tests, fix panic interactions
Modify rangefunc #next protocol to make it more robust
Extra-terrible nests of rangefunc iterators caused the
prior implementation to misbehave non-locally (in outer loops).
Add more rangefunc exit flag tests, parallel and tricky
This tests the assertion that a rangefunc iterator running
in parallel can trigger the race detector if any of the
parallel goroutines attempts an early exit. It also
verifies that if everything else is carefully written,
that it does NOT trigger the race detector if all the
parts run time completion.
Another test tries to rerun a yield function within a loop,
so that any per-line shared checking would be fooled.
Added all the use-of-body/yield-function checking.
These checks handle pathological cases that would cause
rangefunc for loops to behave in surprising ways (compared
to "regular" for loops). For example, a rangefunc iterator
might defer-recover a panic thrown in the syntactic body
of a loop; this notices the fault and panics with an
explanation
Modified closure naming to ID rangefunc bodies
Add a "-range<N>" suffix to the name of any closure generated for
a rangefunc loop body, as provided in Alessandro Arzilli's CL
(which is merged into this one).
Fix return values for panicky range functions
This removes the delayed implementation of "return x" by
ensuring that return values (in rangefunc-return-containing
functions) always have names and translating the "return x"
into "#rv1 = x" where #rv1 is the synthesized name of the
first result.
Updates #61405.
Change-Id: I933299ecce04ceabcf1c0c2de8e610b2ecd1cfd8
Reviewed-on: https://go-review.googlesource.com/c/go/+/584596
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Tim King <taking@google.com>
Diffstat (limited to 'src/runtime/race/testdata')
| -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) +} |
