if !block {
return false
}
- gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2)
+ gopark(nil, nil, waitReasonChanSendNilChan, traceBlockForever, 2)
throw("unreachable")
}
// changes and when we set gp.activeStackChans is not safe for
// stack shrinking.
gp.parkingOnChan.Store(true)
- gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceEvGoBlockSend, 2)
+ gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceBlockChanSend, 2)
// Ensure the value being sent is kept alive until the
// receiver copies it out. The sudog has a pointer to the
// stack object, but sudogs aren't considered as roots of the
if !block {
return
}
- gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2)
+ gopark(nil, nil, waitReasonChanReceiveNilChan, traceBlockForever, 2)
throw("unreachable")
}
// changes and when we set gp.activeStackChans is not safe for
// stack shrinking.
gp.parkingOnChan.Store(true)
- gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceEvGoBlockRecv, 2)
+ gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceBlockChanRecv, 2)
// someone woke us up
if mysg != gp.waiting {
// Park the calling goroutine.
if traceEnabled() {
- traceGoPark(traceEvGoBlock, 1)
+ traceGoPark(traceBlockDebugCall, 1)
}
casGToWaiting(gp, _Grunning, waitReasonDebugCall)
dropg()
notesWithTimeout[n] = noteWithTimeout{gp: gp, deadline: deadline}
releasem(mp)
- gopark(nil, nil, waitReasonSleep, traceEvNone, 1)
+ gopark(nil, nil, waitReasonSleep, traceBlockSleep, 1)
clearTimeoutEvent(id) // note might have woken early, clear timeout
clearIdleID()
notes[n] = gp
releasem(mp)
- gopark(nil, nil, waitReasonZero, traceEvNone, 1)
+ gopark(nil, nil, waitReasonZero, traceBlockGeneric, 1)
mp = acquirem()
delete(notes, n)
// wait until all goroutines are idle
e.returned = true
- gopark(nil, nil, waitReasonZero, traceEvNone, 1)
+ gopark(nil, nil, waitReasonZero, traceBlockGeneric, 1)
events[len(events)-1] = nil
events = events[:len(events)-1]
fb := finq
finq = nil
if fb == nil {
- gopark(finalizercommit, unsafe.Pointer(&finlock), waitReasonFinalizerWait, traceEvGoBlock, 1)
+ gopark(finalizercommit, unsafe.Pointer(&finlock), waitReasonFinalizerWait, traceBlockSystemGoroutine, 1)
continue
}
argRegs = intArgRegs
// Wait until sweep termination, mark, and mark
// termination of cycle N complete.
work.sweepWaiters.list.push(getg())
- goparkunlock(&work.sweepWaiters.lock, waitReasonWaitForGCCycle, traceEvGoBlock, 1)
+ goparkunlock(&work.sweepWaiters.lock, waitReasonWaitForGCCycle, traceBlockUntilGCEnds, 1)
}
}
// Note that at this point, the G may immediately be
// rescheduled and may be running.
return true
- }, unsafe.Pointer(node), waitReasonGCWorkerIdle, traceEvGoBlock, 0)
+ }, unsafe.Pointer(node), waitReasonGCWorkerIdle, traceBlockSystemGoroutine, 0)
// Preemption must not occur here, or another G might see
// p.gcMarkWorkerMode.
return false
}
// Park.
- goparkunlock(&work.assistQueue.lock, waitReasonGCAssistWait, traceEvGoBlockGC, 2)
+ goparkunlock(&work.assistQueue.lock, waitReasonGCAssistWait, traceBlockGCMarkAssist, 2)
return true
}
throw("tried to park scavenger from another goroutine")
}
s.parked = true
- goparkunlock(&s.lock, waitReasonGCScavengeWait, traceEvGoBlock, 2)
+ goparkunlock(&s.lock, waitReasonGCScavengeWait, traceBlockSystemGoroutine, 2)
}
// ready signals to sysmon that the scavenger should be awoken.
// Mark ourselves as asleep and go to sleep.
s.parked = true
- goparkunlock(&s.lock, waitReasonSleep, traceEvGoSleep, 2)
+ goparkunlock(&s.lock, waitReasonSleep, traceBlockSleep, 2)
// How long we actually slept for.
slept = nanotime() - start
lock(&sweep.lock)
sweep.parked = true
c <- 1
- goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1)
+ goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceBlockGCSweep, 1)
for {
// bgsweep attempts to be a "low priority" goroutine by intentionally
continue
}
sweep.parked = true
- goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1)
+ goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceBlockGCSweep, 1)
}
}
// this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl
// do the opposite: store to closing/rd/wd, publishInfo, load of rg/wg
if waitio || netpollcheckerr(pd, mode) == pollNoError {
- gopark(netpollblockcommit, unsafe.Pointer(gpp), waitReasonIOWait, traceEvGoBlockNet, 5)
+ gopark(netpollblockcommit, unsafe.Pointer(gpp), waitReasonIOWait, traceBlockNet, 5)
}
// be careful to not lose concurrent pdReady notification
old := gpp.Swap(pdNil)
}
}
if panicking.Load() != 0 {
- gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1)
+ gopark(nil, nil, waitReasonPanicWait, traceBlockForever, 1)
}
runExitHooks(0)
throw("forcegc: phase error")
}
forcegc.idle.Store(true)
- goparkunlock(&forcegc.lock, waitReasonForceGCIdle, traceEvGoBlock, 1)
+ goparkunlock(&forcegc.lock, waitReasonForceGCIdle, traceBlockSystemGoroutine, 1)
// this goroutine is explicitly resumed by sysmon
if debug.gctrace > 0 {
println("GC forced")
// Reason explains why the goroutine has been parked. It is displayed in stack
// traces and heap dumps. Reasons should be unique and descriptive. Do not
// re-use reasons, add new ones.
-func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) {
+func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceReason traceBlockReason, traceskip int) {
if reason != waitReasonSleep {
checkTimeouts() // timeouts may expire while two goroutines keep the scheduler busy
}
mp.waitlock = lock
mp.waitunlockf = unlockf
gp.waitreason = reason
- mp.waittraceev = traceEv
- mp.waittraceskip = traceskip
+ mp.waitTraceBlockReason = traceReason
+ mp.waitTraceSkip = traceskip
releasem(mp)
// can't do anything that might move the G between Ms here.
mcall(park_m)
// Puts the current goroutine into a waiting state and unlocks the lock.
// The goroutine can be made runnable again by calling goready(gp).
-func goparkunlock(lock *mutex, reason waitReason, traceEv byte, traceskip int) {
- gopark(parkunlock_c, unsafe.Pointer(lock), reason, traceEv, traceskip)
+func goparkunlock(lock *mutex, reason waitReason, traceReason traceBlockReason, traceskip int) {
+ gopark(parkunlock_c, unsafe.Pointer(lock), reason, traceReason, traceskip)
}
func goready(gp *g, traceskip int) {
mp := getg().m
if traceEnabled() {
- traceGoPark(mp.waittraceev, mp.waittraceskip)
+ traceGoPark(mp.waitTraceBlockReason, mp.waitTraceSkip)
}
// N.B. Not using casGToWaiting here because the waitreason is
//go:systemstack
func preemptPark(gp *g) {
if traceEnabled() {
- traceGoPark(traceEvGoBlock, 0)
+ traceGoPark(traceBlockPreempted, 0)
}
status := readgstatus(gp)
if status&^_Gscan != _Grunning {
// wait* are used to carry arguments from gopark into park_m, because
// there's no stack to put them on. That is their sole purpose.
- waitunlockf func(*g, unsafe.Pointer) bool
- waitlock unsafe.Pointer
- waittraceev byte
- waittraceskip int
+ waitunlockf func(*g, unsafe.Pointer) bool
+ waitlock unsafe.Pointer
+ waitTraceBlockReason traceBlockReason
+ waitTraceSkip int
syscalltick uint32
freelink *m // on sched.freem
}
func block() {
- gopark(nil, nil, waitReasonSelectNoCases, traceEvGoStop, 1) // forever
+ gopark(nil, nil, waitReasonSelectNoCases, traceBlockForever, 1) // forever
}
// selectgo implements the select statement.
// changes and when we set gp.activeStackChans is not safe for
// stack shrinking.
gp.parkingOnChan.Store(true)
- gopark(selparkcommit, nil, waitReasonSelect, traceEvGoBlockSelect, 1)
+ gopark(selparkcommit, nil, waitReasonSelect, traceBlockSelect, 1)
gp.activeStackChans = false
sellock(scases, lockorder)
// 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, reason, traceEvGoBlockSync, 4+skipframes)
+ goparkunlock(&root.lock, reason, traceBlockSync, 4+skipframes)
if s.ticket != 0 || cansemacquire(addr) {
break
}
l.tail.next = s
}
l.tail = s
- goparkunlock(&l.lock, waitReasonSyncCondWait, traceEvGoBlockCond, 3)
+ goparkunlock(&l.lock, waitReasonSyncCondWait, traceBlockCondWait, 3)
if t0 != 0 {
blockevent(s.releasetime-t0, 2)
}
if t.nextwhen < 0 { // check for overflow.
t.nextwhen = maxWhen
}
- gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceEvGoSleep, 1)
+ gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceBlockSleep, 1)
}
// resetForSleep is called after the goroutine is parked for timeSleep.
// That means, the max event type value is 63.
)
+// traceBlockReason is an enumeration of reasons a goroutine might block.
+// This is the interface the rest of the runtime uses to tell the
+// tracer why a goroutine blocked. The tracer then propagates this information
+// into the trace however it sees fit.
+//
+// Note that traceBlockReasons should not be compared, since reasons that are
+// distinct by name may *not* be distinct by value.
+type traceBlockReason uint8
+
+// For maximal efficiency, just map the trace block reason directly to a trace
+// event.
+const (
+ traceBlockGeneric traceBlockReason = traceEvGoBlock
+ traceBlockForever = traceEvGoStop
+ traceBlockNet = traceEvGoBlockNet
+ traceBlockSelect = traceEvGoBlockSelect
+ traceBlockCondWait = traceEvGoBlockCond
+ traceBlockSync = traceEvGoBlockSync
+ traceBlockChanSend = traceEvGoBlockSend
+ traceBlockChanRecv = traceEvGoBlockRecv
+ traceBlockGCMarkAssist = traceEvGoBlockGC
+ traceBlockGCSweep = traceEvGoBlock
+ traceBlockSystemGoroutine = traceEvGoBlock
+ traceBlockPreempted = traceEvGoBlock
+ traceBlockDebugCall = traceEvGoBlock
+ traceBlockUntilGCEnds = traceEvGoBlock
+ traceBlockSleep = traceEvGoSleep
+)
+
const (
// Timestamps in trace are cputicks/traceTickDiv.
// This makes absolute values of timestamp diffs smaller,
}
return true
- }, nil, waitReasonTraceReaderBlocked, traceEvGoBlock, 2)
+ }, nil, waitReasonTraceReaderBlocked, traceBlockSystemGoroutine, 2)
goto top
}
traceEvent(traceEvGoPreempt, 1)
}
-func traceGoPark(traceEv byte, skip int) {
- traceEvent(traceEv, skip)
+func traceGoPark(reason traceBlockReason, skip int) {
+ // Convert the block reason directly to a trace event type.
+ // See traceBlockReason for more information.
+ traceEvent(byte(reason), skip)
}
func traceGoUnpark(gp *g, skip int) {