mirror of
https://github.com/golang/go
synced 2024-11-15 07:40:47 -07:00
f2d243db8f
Pull2 tests are failing with -race, giving false-positive race conditions
due to bad race instrumentation.
No tests for this as it should be caught by the race builders. The only
reason it was not caught is because it is behind GOEXPERIMENT=rangefunc.
Fixes #64651
Change-Id: I20554da930b0e19594e0e267f01a1e7a9cbc577a
GitHub-Last-Rev: 7c1f19238d
GitHub-Pull-Request: golang/go#64653
Reviewed-on: https://go-review.googlesource.com/c/go/+/548895
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
170 lines
4.6 KiB
Go
170 lines
4.6 KiB
Go
// Copyright 2023 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 iter provides basic definitions and operations
|
|
// related to iteration in Go.
|
|
//
|
|
// This package is experimental and can only be imported
|
|
// when building with GOEXPERIMENT=rangefunc.
|
|
package iter
|
|
|
|
import (
|
|
"internal/race"
|
|
"unsafe"
|
|
)
|
|
|
|
// Seq is an iterator over sequences of individual values.
|
|
// When called as seq(yield), seq calls yield(v) for each value v in the sequence,
|
|
// stopping early if yield returns false.
|
|
type Seq[V any] func(yield func(V) bool)
|
|
|
|
// Seq2 is an iterator over sequences of pairs of values, most commonly key-value pairs.
|
|
// When called as seq(yield), seq calls yield(k, v) for each pair (k, v) in the sequence,
|
|
// stopping early if yield returns false.
|
|
type Seq2[K, V any] func(yield func(K, V) bool)
|
|
|
|
type coro struct{}
|
|
|
|
//go:linkname newcoro runtime.newcoro
|
|
func newcoro(func(*coro)) *coro
|
|
|
|
//go:linkname coroswitch runtime.coroswitch
|
|
func coroswitch(*coro)
|
|
|
|
// Pull converts the “push-style” iterator sequence seq
|
|
// into a “pull-style” iterator accessed by the two functions
|
|
// next and stop.
|
|
//
|
|
// Next returns the next value in the sequence
|
|
// and a boolean indicating whether the value is valid.
|
|
// When the sequence is over, next returns the zero V and false.
|
|
// It is valid to call next after reaching the end of the sequence
|
|
// or after calling stop. These calls will continue
|
|
// to return the zero V and false.
|
|
//
|
|
// Stop ends the iteration. It must be called when the caller is
|
|
// no longer interested in next values and next has not yet
|
|
// signaled that the sequence is over (with a false boolean return).
|
|
// It is valid to call stop multiple times and when next has
|
|
// already returned false.
|
|
//
|
|
// It is an error to call next or stop from multiple goroutines
|
|
// simultaneously.
|
|
func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) {
|
|
var (
|
|
v V
|
|
ok bool
|
|
done bool
|
|
racer int
|
|
)
|
|
c := newcoro(func(c *coro) {
|
|
race.Acquire(unsafe.Pointer(&racer))
|
|
yield := func(v1 V) bool {
|
|
if done {
|
|
return false
|
|
}
|
|
v, ok = v1, true
|
|
race.Release(unsafe.Pointer(&racer))
|
|
coroswitch(c)
|
|
race.Acquire(unsafe.Pointer(&racer))
|
|
return !done
|
|
}
|
|
seq(yield)
|
|
var v0 V
|
|
v, ok = v0, false
|
|
done = true
|
|
race.Release(unsafe.Pointer(&racer))
|
|
})
|
|
next = func() (v1 V, ok1 bool) {
|
|
race.Write(unsafe.Pointer(&racer)) // detect races
|
|
if done {
|
|
return
|
|
}
|
|
race.Release(unsafe.Pointer(&racer))
|
|
coroswitch(c)
|
|
race.Acquire(unsafe.Pointer(&racer))
|
|
return v, ok
|
|
}
|
|
stop = func() {
|
|
race.Write(unsafe.Pointer(&racer)) // detect races
|
|
if !done {
|
|
done = true
|
|
race.Release(unsafe.Pointer(&racer))
|
|
coroswitch(c)
|
|
race.Acquire(unsafe.Pointer(&racer))
|
|
}
|
|
}
|
|
return next, stop
|
|
}
|
|
|
|
// Pull2 converts the “push-style” iterator sequence seq
|
|
// into a “pull-style” iterator accessed by the two functions
|
|
// next and stop.
|
|
//
|
|
// Next returns the next pair in the sequence
|
|
// and a boolean indicating whether the pair is valid.
|
|
// When the sequence is over, next returns a pair of zero values and false.
|
|
// It is valid to call next after reaching the end of the sequence
|
|
// or after calling stop. These calls will continue
|
|
// to return a pair of zero values and false.
|
|
//
|
|
// Stop ends the iteration. It must be called when the caller is
|
|
// no longer interested in next values and next has not yet
|
|
// signaled that the sequence is over (with a false boolean return).
|
|
// It is valid to call stop multiple times and when next has
|
|
// already returned false.
|
|
//
|
|
// It is an error to call next or stop from multiple goroutines
|
|
// simultaneously.
|
|
func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) {
|
|
var (
|
|
k K
|
|
v V
|
|
ok bool
|
|
done bool
|
|
racer int
|
|
)
|
|
c := newcoro(func(c *coro) {
|
|
race.Acquire(unsafe.Pointer(&racer))
|
|
yield := func(k1 K, v1 V) bool {
|
|
if done {
|
|
return false
|
|
}
|
|
k, v, ok = k1, v1, true
|
|
race.Release(unsafe.Pointer(&racer))
|
|
coroswitch(c)
|
|
race.Acquire(unsafe.Pointer(&racer))
|
|
return !done
|
|
}
|
|
seq(yield)
|
|
var k0 K
|
|
var v0 V
|
|
k, v, ok = k0, v0, false
|
|
done = true
|
|
race.Release(unsafe.Pointer(&racer))
|
|
})
|
|
next = func() (k1 K, v1 V, ok1 bool) {
|
|
race.Write(unsafe.Pointer(&racer)) // detect races
|
|
if done {
|
|
return
|
|
}
|
|
race.Release(unsafe.Pointer(&racer))
|
|
coroswitch(c)
|
|
race.Acquire(unsafe.Pointer(&racer))
|
|
return k, v, ok
|
|
}
|
|
stop = func() {
|
|
race.Write(unsafe.Pointer(&racer)) // detect races
|
|
if !done {
|
|
done = true
|
|
race.Release(unsafe.Pointer(&racer))
|
|
coroswitch(c)
|
|
race.Acquire(unsafe.Pointer(&racer))
|
|
}
|
|
}
|
|
return next, stop
|
|
}
|