// directly if possible.
setGNoWB(&mp.curg, gnext)
setMNoWB(&gnext.m, mp)
+
+ // Synchronize with any out-standing goroutine profile. We're about to start
+ // executing, and an invariant of the profiler is that we tryRecordGoroutineProfile
+ // whenever a goroutine is about to start running.
+ //
+ // N.B. We must do this before transitioning to _Grunning but after installing gnext
+ // in curg, so that we have a valid curg for allocation (tryRecordGoroutineProfile
+ // may allocate).
+ if goroutineProfile.active {
+ tryRecordGoroutineProfile(gnext, nil, osyield)
+ }
+
if !gnext.atomicstatus.CompareAndSwap(_Gwaiting, _Grunning) {
// The CAS failed: use casgstatus, which will take care of
// coordinating with the garbage collector about the state change.
"internal/syscall/unix"
"internal/testenv"
"io"
+ "iter"
"math"
"math/big"
"os"
}
}
+// Regression test for #69998.
+func TestGoroutineProfileCoro(t *testing.T) {
+ testenv.MustHaveParallelism(t)
+
+ goroutineProf := Lookup("goroutine")
+
+ // Set up a goroutine to just create and run coroutine goroutines all day.
+ iterFunc := func() {
+ p, stop := iter.Pull2(
+ func(yield func(int, int) bool) {
+ for i := 0; i < 10000; i++ {
+ if !yield(i, i) {
+ return
+ }
+ }
+ },
+ )
+ defer stop()
+ for {
+ _, _, ok := p()
+ if !ok {
+ break
+ }
+ }
+ }
+ var wg sync.WaitGroup
+ done := make(chan struct{})
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for {
+ iterFunc()
+ select {
+ case <-done:
+ default:
+ }
+ }
+ }()
+
+ // Take a goroutine profile. If the bug in #69998 is present, this will crash
+ // with high probability. We don't care about the output for this bug.
+ goroutineProf.WriteTo(io.Discard, 1)
+}
+
func BenchmarkGoroutine(b *testing.B) {
withIdle := func(n int, fn func(b *testing.B)) func(b *testing.B) {
return func(b *testing.B) {