]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: fix double wakeup in CPU profile buffer
authorNick Ripley <nick.ripley@datadoghq.com>
Fri, 21 Nov 2025 14:09:57 +0000 (14:09 +0000)
committerNick Ripley <nick.ripley@datadoghq.com>
Fri, 21 Nov 2025 21:24:46 +0000 (13:24 -0800)
The profBuf.wakeupExtra method wakes up the profile reader if it's
sleeping, either when the buffer is closed or when there is a pending
overflow entry.  Unlike in profBuf.write, profBuf.wakeupExtra does not
clear the profReaderSleeping bit before doing the wakeup. As a result,
if there are two writes to a full buffer before the sleeping reader has
time to wake up, we'll see two consecutive calls to notewakeup, which is
a fatal error. This CL updates profBuf.wakeupExtra to clear the sleeping
bit before doing the wakeup.

This CL adds a unit test that demonstrates the problem. This is
theoretically possible to trigger for real programs as well, but it's
more difficult. The profBufWordCount is large enough that it takes
several CPU-seconds to fill up the buffer. So we'd need to run on a
system with lots of cores to have a chance of running into this failure,
and the reader would need to fully go to sleep before a large burst of
CPU activity.

Change-Id: I59b4fa86a12f6236890b82cd353a95706a6a6964
Reviewed-on: https://go-review.googlesource.com/c/go/+/722940
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Mark Freeman <markfreeman@google.com>
src/runtime/profbuf.go
src/runtime/profbuf_test.go

index 8ae626b1b08594f947d19f2e3ead4ff193bba9db..40aff9e1d2425131764446bc50bd8d56907a0e8b 100644 (file)
@@ -406,6 +406,11 @@ func (b *profBuf) wakeupExtra() {
        for {
                old := b.w.load()
                new := old | profWriteExtra
+               // Clear profReaderSleeping. We're going to wake up the reader
+               // if it was sleeping and we don't want double wakeups in case
+               // we, for example, attempt to write into a full buffer multiple
+               // times before the reader wakes up.
+               new &^= profReaderSleeping
                if !b.w.cas(old, new) {
                        continue
                }
index 9050d1fa25faa81d1b94ea0c528d18b3c16be5f6..7a435c9e5f9f3de8c815556c8c599391ba2b10df 100644 (file)
@@ -174,3 +174,19 @@ func TestProfBuf(t *testing.T) {
                }
        })
 }
+
+func TestProfBufDoubleWakeup(t *testing.T) {
+       b := NewProfBuf(2, 16, 2)
+       go func() {
+               for range 1000 {
+                       b.Write(nil, 1, []uint64{5, 6}, []uintptr{7, 8})
+               }
+               b.Close()
+       }()
+       for {
+               _, _, eof := b.Read(ProfBufBlocking)
+               if eof {
+                       return
+               }
+       }
+}