Skip to content

Commit b604148

Browse files
committed
runtime: fix double wakeup in CPU profile buffer
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 <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> Reviewed-by: Mark Freeman <[email protected]>
1 parent 22f24f9 commit b604148

File tree

2 files changed

+21
-0
lines changed

2 files changed

+21
-0
lines changed

src/runtime/profbuf.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,11 @@ func (b *profBuf) wakeupExtra() {
406406
for {
407407
old := b.w.load()
408408
new := old | profWriteExtra
409+
// Clear profReaderSleeping. We're going to wake up the reader
410+
// if it was sleeping and we don't want double wakeups in case
411+
// we, for example, attempt to write into a full buffer multiple
412+
// times before the reader wakes up.
413+
new &^= profReaderSleeping
409414
if !b.w.cas(old, new) {
410415
continue
411416
}

src/runtime/profbuf_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,19 @@ func TestProfBuf(t *testing.T) {
174174
}
175175
})
176176
}
177+
178+
func TestProfBufDoubleWakeup(t *testing.T) {
179+
b := NewProfBuf(2, 16, 2)
180+
go func() {
181+
for range 1000 {
182+
b.Write(nil, 1, []uint64{5, 6}, []uintptr{7, 8})
183+
}
184+
b.Close()
185+
}()
186+
for {
187+
_, _, eof := b.Read(ProfBufBlocking)
188+
if eof {
189+
return
190+
}
191+
}
192+
}

0 commit comments

Comments
 (0)