mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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 <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Mark Freeman <markfreeman@google.com>
This commit is contained in:
parent
22f24f90b5
commit
b604148c4e
2 changed files with 21 additions and 0 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue