Source file src/runtime/race/testdata/rangefunc_test.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package race_test
     6  
     7  import (
     8  	"runtime"
     9  	"sync/atomic"
    10  	"testing"
    11  )
    12  
    13  type Seq2[T1, T2 any] func(yield func(T1, T2) bool)
    14  
    15  // ofSliceIndex returns a Seq over the elements of s. It is equivalent
    16  // to range s, except that it splits s into two halves and iterates
    17  // in two separate goroutines.  This is racy if yield is racy, and yield
    18  // will be racy if it contains an early exit.
    19  func ofSliceIndex[T any, S ~[]T](s S) Seq2[int, T] {
    20  	return func(yield func(int, T) bool) {
    21  		c := make(chan bool, 2)
    22  		var done atomic.Bool
    23  		go func() {
    24  			for i := 0; i < len(s)/2; i++ {
    25  				if !done.Load() && !yield(i, s[i]) {
    26  					done.Store(true)
    27  					c <- false
    28  				}
    29  			}
    30  			c <- true
    31  		}()
    32  		go func() {
    33  			for i := len(s) / 2; i < len(s); i++ {
    34  				if !done.Load() && !yield(i, s[i]) {
    35  					done.Store(true)
    36  					c <- false
    37  				}
    38  			}
    39  			c <- true
    40  			return
    41  		}()
    42  		if !<-c {
    43  			return
    44  		}
    45  		<-c
    46  	}
    47  }
    48  
    49  // foo is racy, or not, depending on the value of v
    50  // (0-4 == racy, otherwise, not racy).
    51  func foo(v int) int64 {
    52  	var asum atomic.Int64
    53  	for i, x := range ofSliceIndex([]int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) {
    54  		if i%5 == v {
    55  			break
    56  		}
    57  		asum.Add(x) // don't race on asum
    58  		runtime.Gosched()
    59  	}
    60  	return 100 + asum.Load()
    61  }
    62  
    63  // TestRaceRangeFuncIterator races because x%5 can be equal to 4,
    64  // therefore foo can early exit.
    65  func TestRaceRangeFuncIterator(t *testing.T) {
    66  	t.Skip("#72925: uncaught panic ends tests")
    67  	x := foo(4)
    68  	t.Logf("foo(4)=%d", x)
    69  }
    70  
    71  // TestNoRaceRangeFuncIterator does not race because x%5 is never 5,
    72  // therefore foo's loop will not exit early, and this it will not race.
    73  func TestNoRaceRangeFuncIterator(t *testing.T) {
    74  	t.Skip("#72925: unexpected data race")
    75  	x := foo(5)
    76  	t.Logf("foo(5)=%d", x)
    77  }
    78  

View as plain text