mirror of
https://github.com/golang/go
synced 2024-11-22 15:34:53 -07:00
runtime: make the wait reason for a g blocked on a mutex more specific
This change adds 3 new waitReasons that correspond to sync.Mutex.Lock, sync.RWMutex.RLock, and sync.RWMutex.Lock that are plumbed down into semacquire1 by exporting new functions to the sync package from the runtime. Currently these three functions show up as "semacquire" in backtraces which isn't very clear, though the stack trace itself should reveal what's really going on. This represents a minor improvement to backtrace readability, though blocking on an RWMutex.w.Lock will still show up as blocking on a regular mutex (I suppose technically it is). This is a step toward helping the runtime identify when a goroutine is blocked on a mutex of some kind. For #49881. Change-Id: Ia409b4d27e117fe4bfdc25fa541e9c58d6d587b9 Reviewed-on: https://go-review.googlesource.com/c/go/+/427616 TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Michael Knyszek <mknyszek@google.com> Reviewed-by: Michael Pratt <mpratt@google.com> Run-TryBot: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
parent
b7c28f484d
commit
a2c396ce00
@ -40,7 +40,7 @@ func metricsLock() {
|
||||
// Acquire the metricsSema but with handoff. Operations are typically
|
||||
// expensive enough that queueing up goroutines and handing off between
|
||||
// them will be noticeably better-behaved.
|
||||
semacquire1(&metricsSema, true, 0, 0)
|
||||
semacquire1(&metricsSema, true, 0, 0, waitReasonSemacquire)
|
||||
if raceenabled {
|
||||
raceacquire(unsafe.Pointer(&metricsSema))
|
||||
}
|
||||
|
@ -1089,7 +1089,7 @@ func blockMutex(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
mu.Lock()
|
||||
go func() {
|
||||
awaitBlockedGoroutine(t, "semacquire", "blockMutex")
|
||||
awaitBlockedGoroutine(t, "sync.Mutex.Lock", "blockMutex")
|
||||
mu.Unlock()
|
||||
}()
|
||||
// Note: Unlock releases mu before recording the mutex event,
|
||||
|
@ -1054,7 +1054,9 @@ const (
|
||||
waitReasonSemacquire // "semacquire"
|
||||
waitReasonSleep // "sleep"
|
||||
waitReasonSyncCondWait // "sync.Cond.Wait"
|
||||
waitReasonTimerGoroutineIdle // "timer goroutine (idle)"
|
||||
waitReasonSyncMutexLock // "sync.Mutex.Lock"
|
||||
waitReasonSyncRWMutexRLock // "sync.RWMutex.RLock"
|
||||
waitReasonSyncRWMutexLock // "sync.RWMutex.Lock"
|
||||
waitReasonTraceReaderBlocked // "trace reader (blocked)"
|
||||
waitReasonWaitForGCCycle // "wait for GC cycle"
|
||||
waitReasonGCWorkerIdle // "GC worker (idle)"
|
||||
@ -1084,7 +1086,9 @@ var waitReasonStrings = [...]string{
|
||||
waitReasonSemacquire: "semacquire",
|
||||
waitReasonSleep: "sleep",
|
||||
waitReasonSyncCondWait: "sync.Cond.Wait",
|
||||
waitReasonTimerGoroutineIdle: "timer goroutine (idle)",
|
||||
waitReasonSyncMutexLock: "sync.Mutex.Lock",
|
||||
waitReasonSyncRWMutexRLock: "sync.RWMutex.RLock",
|
||||
waitReasonSyncRWMutexLock: "sync.RWMutex.Lock",
|
||||
waitReasonTraceReaderBlocked: "trace reader (blocked)",
|
||||
waitReasonWaitForGCCycle: "wait for GC cycle",
|
||||
waitReasonGCWorkerIdle: "GC worker (idle)",
|
||||
|
@ -59,12 +59,12 @@ func (t *semTable) rootFor(addr *uint32) *semaRoot {
|
||||
|
||||
//go:linkname sync_runtime_Semacquire sync.runtime_Semacquire
|
||||
func sync_runtime_Semacquire(addr *uint32) {
|
||||
semacquire1(addr, false, semaBlockProfile, 0)
|
||||
semacquire1(addr, false, semaBlockProfile, 0, waitReasonSemacquire)
|
||||
}
|
||||
|
||||
//go:linkname poll_runtime_Semacquire internal/poll.runtime_Semacquire
|
||||
func poll_runtime_Semacquire(addr *uint32) {
|
||||
semacquire1(addr, false, semaBlockProfile, 0)
|
||||
semacquire1(addr, false, semaBlockProfile, 0, waitReasonSemacquire)
|
||||
}
|
||||
|
||||
//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease
|
||||
@ -74,7 +74,17 @@ func sync_runtime_Semrelease(addr *uint32, handoff bool, skipframes int) {
|
||||
|
||||
//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex
|
||||
func sync_runtime_SemacquireMutex(addr *uint32, lifo bool, skipframes int) {
|
||||
semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes)
|
||||
semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes, waitReasonSyncMutexLock)
|
||||
}
|
||||
|
||||
//go:linkname sync_runtime_SemacquireRWMutexR sync.runtime_SemacquireRWMutexR
|
||||
func sync_runtime_SemacquireRWMutexR(addr *uint32, lifo bool, skipframes int) {
|
||||
semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes, waitReasonSyncRWMutexRLock)
|
||||
}
|
||||
|
||||
//go:linkname sync_runtime_SemacquireRWMutex sync.runtime_SemacquireRWMutex
|
||||
func sync_runtime_SemacquireRWMutex(addr *uint32, lifo bool, skipframes int) {
|
||||
semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile, skipframes, waitReasonSyncRWMutexLock)
|
||||
}
|
||||
|
||||
//go:linkname poll_runtime_Semrelease internal/poll.runtime_Semrelease
|
||||
@ -98,10 +108,10 @@ const (
|
||||
|
||||
// Called from runtime.
|
||||
func semacquire(addr *uint32) {
|
||||
semacquire1(addr, false, 0, 0)
|
||||
semacquire1(addr, false, 0, 0, waitReasonSemacquire)
|
||||
}
|
||||
|
||||
func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags, skipframes int) {
|
||||
func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags, skipframes int, reason waitReason) {
|
||||
gp := getg()
|
||||
if gp != gp.m.curg {
|
||||
throw("semacquire not on the G stack")
|
||||
@ -147,7 +157,7 @@ func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags, skipframes i
|
||||
// Any semrelease after the cansemacquire knows we're waiting
|
||||
// (we set nwait above), so go to sleep.
|
||||
root.queue(addr, s, lifo)
|
||||
goparkunlock(&root.lock, waitReasonSemacquire, traceEvGoBlockSync, 4+skipframes)
|
||||
goparkunlock(&root.lock, reason, traceEvGoBlockSync, 4+skipframes)
|
||||
if s.ticket != 0 || cansemacquire(addr) {
|
||||
break
|
||||
}
|
||||
|
@ -13,11 +13,17 @@ import "unsafe"
|
||||
// library and should not be used directly.
|
||||
func runtime_Semacquire(s *uint32)
|
||||
|
||||
// SemacquireMutex is like Semacquire, but for profiling contended Mutexes.
|
||||
// Semacquire(RW)Mutex(R) is like Semacquire, but for profiling contended
|
||||
// Mutexes and RWMutexes.
|
||||
// If lifo is true, queue waiter at the head of wait queue.
|
||||
// skipframes is the number of frames to omit during tracing, counting from
|
||||
// runtime_SemacquireMutex's caller.
|
||||
// The different forms of this function just tell the runtime how to present
|
||||
// the reason for waiting in a backtrace, and is used to compute some metrics.
|
||||
// Otherwise they're functionally identical.
|
||||
func runtime_SemacquireMutex(s *uint32, lifo bool, skipframes int)
|
||||
func runtime_SemacquireRWMutexR(s *uint32, lifo bool, skipframes int)
|
||||
func runtime_SemacquireRWMutex(s *uint32, lifo bool, skipframes int)
|
||||
|
||||
// Semrelease atomically increments *s and notifies a waiting goroutine
|
||||
// if one is blocked in Semacquire.
|
||||
|
@ -68,7 +68,7 @@ func (rw *RWMutex) RLock() {
|
||||
}
|
||||
if rw.readerCount.Add(1) < 0 {
|
||||
// A writer is pending, wait for it.
|
||||
runtime_SemacquireMutex(&rw.readerSem, false, 0)
|
||||
runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)
|
||||
}
|
||||
if race.Enabled {
|
||||
race.Enable()
|
||||
@ -149,7 +149,7 @@ func (rw *RWMutex) Lock() {
|
||||
r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders
|
||||
// Wait for active readers.
|
||||
if r != 0 && rw.readerWait.Add(r) != 0 {
|
||||
runtime_SemacquireMutex(&rw.writerSem, false, 0)
|
||||
runtime_SemacquireRWMutex(&rw.writerSem, false, 0)
|
||||
}
|
||||
if race.Enabled {
|
||||
race.Enable()
|
||||
|
Loading…
Reference in New Issue
Block a user