if !block {
return false
}
- gopark(nil, nil, "chan send (nil chan)", traceEvGoStop, 2)
+ gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2)
throw("unreachable")
}
gp.waiting = mysg
gp.param = nil
c.sendq.enqueue(mysg)
- goparkunlock(&c.lock, "chan send", traceEvGoBlockSend, 3)
+ goparkunlock(&c.lock, waitReasonChanSend, traceEvGoBlockSend, 3)
// someone woke us up.
if mysg != gp.waiting {
if !block {
return
}
- gopark(nil, nil, "chan receive (nil chan)", traceEvGoStop, 2)
+ gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2)
throw("unreachable")
}
mysg.c = c
gp.param = nil
c.recvq.enqueue(mysg)
- goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3)
+ goparkunlock(&c.lock, waitReasonChanReceive, traceEvGoBlockRecv, 3)
// someone woke us up
if mysg != gp.waiting {
dumpbool(isSystemGoroutine(gp))
dumpbool(false) // isbackground
dumpint(uint64(gp.waitsince))
- dumpstr(gp.waitreason)
+ dumpstr(gp.waitreason.String())
dumpint(uint64(uintptr(gp.sched.ctxt)))
dumpint(uint64(uintptr(unsafe.Pointer(gp.m))))
dumpint(uint64(uintptr(unsafe.Pointer(gp._defer))))
func writeheapdump_m(fd uintptr) {
_g_ := getg()
casgstatus(_g_.m.curg, _Grunning, _Gwaiting)
- _g_.waitreason = "dumping heap"
+ _g_.waitreason = waitReasonDumpingHeap
// Update stats so we can dump them.
// As a side effect, flushes all the MCaches so the MSpan.freelist
gp := getg()
fing = gp
fingwait = true
- goparkunlock(&finlock, "finalizer wait", traceEvGoBlock, 1)
+ goparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1)
continue
}
unlock(&finlock)
gp := getg()
gp.schedlink = work.sweepWaiters.head
work.sweepWaiters.head.set(gp)
- goparkunlock(&work.sweepWaiters.lock, "wait for GC cycle", traceEvGoBlock, 1)
+ goparkunlock(&work.sweepWaiters.lock, waitReasonWaitForGCCycle, traceEvGoBlock, 1)
}
}
_g_.m.traceback = 2
gp := _g_.m.curg
casgstatus(gp, _Grunning, _Gwaiting)
- gp.waitreason = "garbage collection"
+ gp.waitreason = waitReasonGarbageCollection
// Run gc on the g0 stack. We do this so that the g stack
// we're currently running on will no longer change. Cuts
}
}
return true
- }, unsafe.Pointer(park), "GC worker (idle)", traceEvGoBlock, 0)
+ }, unsafe.Pointer(park), waitReasonGCWorkerIdle, traceEvGoBlock, 0)
// Loop until the P dies and disassociates this
// worker (the P may later be reused, in which case
selfScan := gp == userG && readgstatus(userG) == _Grunning
if selfScan {
casgstatus(userG, _Grunning, _Gwaiting)
- userG.waitreason = "garbage collection scan"
+ userG.waitreason = waitReasonGarbageCollectionScan
}
// TODO: scang blocks until gp's stack has
// gcDrainN requires the caller to be preemptible.
casgstatus(gp, _Grunning, _Gwaiting)
- gp.waitreason = "GC assist marking"
+ gp.waitreason = waitReasonGCAssistMarking
// drain own cached work first in the hopes that it
// will be more cache friendly.
return false
}
// Park.
- goparkunlock(&work.assistQueue.lock, "GC assist wait", traceEvGoBlockGC, 2)
+ goparkunlock(&work.assistQueue.lock, waitReasonGCAssistWait, traceEvGoBlockGC, 2)
return true
}
lock(&sweep.lock)
sweep.parked = true
c <- 1
- goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1)
+ goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1)
for {
for gosweepone() != ^uintptr(0) {
continue
}
sweep.parked = true
- goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1)
+ goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1)
}
}
// this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl
// do the opposite: store to closing/rd/wd, membarrier, load of rg/wg
if waitio || netpollcheckerr(pd, mode) == 0 {
- gopark(netpollblockcommit, unsafe.Pointer(gpp), "IO wait", traceEvGoBlockNet, 5)
+ gopark(netpollblockcommit, unsafe.Pointer(gpp), waitReasonIOWait, traceEvGoBlockNet, 5)
}
// be careful to not lose concurrent READY notification
old := atomic.Xchguintptr(gpp, 0)
}
}
if atomic.Load(&panicking) != 0 {
- gopark(nil, nil, "panicwait", traceEvGoStop, 1)
+ gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1)
}
exit(0)
throw("forcegc: phase error")
}
atomic.Store(&forcegc.idle, 1)
- goparkunlock(&forcegc.lock, "force gc (idle)", traceEvGoBlock, 1)
+ goparkunlock(&forcegc.lock, waitReasonForceGGIdle, traceEvGoBlock, 1)
// this goroutine is explicitly resumed by sysmon
if debug.gctrace > 0 {
println("GC forced")
// If unlockf returns false, the goroutine is resumed.
// unlockf must not access this G's stack, as it may be moved between
// the call to gopark and the call to unlockf.
-func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason string, traceEv byte, traceskip int) {
+// 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) {
mp := acquirem()
gp := mp.curg
status := readgstatus(gp)
// 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 string, traceEv byte, traceskip int) {
+func goparkunlock(lock *mutex, reason waitReason, traceEv byte, traceskip int) {
gopark(parkunlock_c, unsafe.Pointer(lock), reason, traceEv, traceskip)
}
gp._defer = nil // should be true already but just in case.
gp._panic = nil // non-nil for Goexit during panic. points at stack-allocated data.
gp.writebuf = nil
- gp.waitreason = ""
+ gp.waitreason = 0
gp.param = nil
gp.labels = nil
gp.timer = nil
if lockedm != nil {
id2 = lockedm.id
}
- print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason, ") m=", id1, " lockedm=", id2, "\n")
+ print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason.String(), ") m=", id1, " lockedm=", id2, "\n")
}
unlock(&allglock)
unlock(&sched.lock)
atomicstatus uint32
stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
goid int64
- waitsince int64 // approx time when the g become blocked
- waitreason string // if status==Gwaiting
schedlink guintptr
- preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
- paniconfault bool // panic (instead of crash) on unexpected fault address
- preemptscan bool // preempted g does scan for gc
- gcscandone bool // g has scanned stack; protected by _Gscan bit in status
- gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; TODO: remove?
- throwsplit bool // must not split stack
- raceignore int8 // ignore race detection events
- sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
- sysexitticks int64 // cputicks when syscall has returned (for tracing)
- traceseq uint64 // trace event sequencer
- tracelastp puintptr // last P emitted an event for this goroutine
+ waitsince int64 // approx time when the g become blocked
+ waitreason waitReason // if status==Gwaiting
+ preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
+ paniconfault bool // panic (instead of crash) on unexpected fault address
+ preemptscan bool // preempted g does scan for gc
+ gcscandone bool // g has scanned stack; protected by _Gscan bit in status
+ gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; TODO: remove?
+ throwsplit bool // must not split stack
+ raceignore int8 // ignore race detection events
+ sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
+ sysexitticks int64 // cputicks when syscall has returned (for tracing)
+ traceseq uint64 // trace event sequencer
+ tracelastp puintptr // last P emitted an event for this goroutine
lockedm muintptr
sig uint32
writebuf []byte
// The maximum number of frames we print for a traceback
const _TracebackMaxFrames = 100
+// A waitReason explains why a goroutine has been stopped.
+// See gopark. Do not re-use waitReasons, add new ones.
+type waitReason uint8
+
+const (
+ waitReasonZero waitReason = iota // ""
+ waitReasonGCAssistMarking // "GC assist marking"
+ waitReasonIOWait // "IO wait"
+ waitReasonChanReceiveNilChan // "chan receive (nil chan)"
+ waitReasonChanSendNilChan // "chan send (nil chan)"
+ waitReasonDumpingHeap // "dumping heap"
+ waitReasonGarbageCollection // "garbage collection"
+ waitReasonGarbageCollectionScan // "garbage collection scan"
+ waitReasonPanicWait // "panicwait"
+ waitReasonSelect // "select"
+ waitReasonSelectNoCases // "select (no cases)"
+ waitReasonGCAssistWait // "GC assist wait"
+ waitReasonGCSweepWait // "GC sweep wait"
+ waitReasonChanReceive // "chan receive"
+ waitReasonChanSend // "chan send"
+ waitReasonFinalizerWait // "finalizer wait"
+ waitReasonForceGGIdle // "force gc (idle)"
+ waitReasonSemacquire // "semacquire"
+ waitReasonSleep // "sleep"
+ waitReasonSyncCondWait // "sync.Cond.Wait"
+ waitReasonTimerGoroutineIdle // "timer goroutine (idle)"
+ waitReasonTraceReaderBlocked // "trace reader (blocked)"
+ waitReasonWaitForGCCycle // "wait for GC cycle"
+ waitReasonGCWorkerIdle // "GC worker (idle)"
+)
+
+var waitReasonStrings = [...]string{
+ waitReasonZero: "",
+ waitReasonGCAssistMarking: "GC assist marking",
+ waitReasonIOWait: "IO wait",
+ waitReasonChanReceiveNilChan: "chan receive (nil chan)",
+ waitReasonChanSendNilChan: "chan send (nil chan)",
+ waitReasonDumpingHeap: "dumping heap",
+ waitReasonGarbageCollection: "garbage collection",
+ waitReasonGarbageCollectionScan: "garbage collection scan",
+ waitReasonPanicWait: "panicwait",
+ waitReasonSelect: "select",
+ waitReasonSelectNoCases: "select (no cases)",
+ waitReasonGCAssistWait: "GC assist wait",
+ waitReasonGCSweepWait: "GC sweep wait",
+ waitReasonChanReceive: "chan receive",
+ waitReasonChanSend: "chan send",
+ waitReasonFinalizerWait: "finalizer wait",
+ waitReasonForceGGIdle: "force gc (idle)",
+ waitReasonSemacquire: "semacquire",
+ waitReasonSleep: "sleep",
+ waitReasonSyncCondWait: "sync.Cond.Wait",
+ waitReasonTimerGoroutineIdle: "timer goroutine (idle)",
+ waitReasonTraceReaderBlocked: "trace reader (blocked)",
+ waitReasonWaitForGCCycle: "wait for GC cycle",
+ waitReasonGCWorkerIdle: "GC worker (idle)",
+}
+
+func (w waitReason) String() string {
+ if w < 0 || w >= waitReason(len(waitReasonStrings)) {
+ return "unknown wait reason"
+ }
+ return waitReasonStrings[w]
+}
+
var (
allglen uintptr
allm *m
}
func block() {
- gopark(nil, nil, "select (no cases)", traceEvGoStop, 1) // forever
+ gopark(nil, nil, waitReasonSelectNoCases, traceEvGoStop, 1) // forever
}
// selectgo implements the select statement.
// wait for someone to wake us up
gp.param = nil
- gopark(selparkcommit, nil, "select", traceEvGoBlockSelect, 1)
+ gopark(selparkcommit, nil, waitReasonSelect, traceEvGoBlockSelect, 1)
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, "semacquire", traceEvGoBlockSync, 4)
+ goparkunlock(&root.lock, waitReasonSemacquire, traceEvGoBlockSync, 4)
if s.ticket != 0 || cansemacquire(addr) {
break
}
l.tail.next = s
}
l.tail = s
- goparkunlock(&l.lock, "sync.Cond.Wait", traceEvGoBlockCond, 3)
+ goparkunlock(&l.lock, waitReasonSyncCondWait, traceEvGoBlockCond, 3)
if t0 != 0 {
blockevent(s.releasetime-t0, 2)
}
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
- {runtime.G{}, 220, 384}, // g, but exported for testing
+ {runtime.G{}, 216, 376}, // g, but exported for testing
}
for _, tt := range tests {
tb := t.assignBucket()
lock(&tb.lock)
tb.addtimerLocked(t)
- goparkunlock(&tb.lock, "sleep", traceEvGoSleep, 2)
+ goparkunlock(&tb.lock, waitReasonSleep, traceEvGoSleep, 2)
}
// startTimer adds t to the timer heap.
if delta < 0 || faketime > 0 {
// No timers left - put goroutine to sleep.
tb.rescheduling = true
- goparkunlock(&tb.lock, "timer goroutine (idle)", traceEvGoBlock, 1)
+ goparkunlock(&tb.lock, waitReasonTimerGoroutineIdle, traceEvGoBlock, 1)
continue
}
// At least one timer pending. Sleep until then.
// Wait for new data.
if trace.fullHead == 0 && !trace.shutdown {
trace.reader.set(getg())
- goparkunlock(&trace.lock, "trace reader (blocked)", traceEvGoBlock, 2)
+ goparkunlock(&trace.lock, waitReasonTraceReaderBlocked, traceEvGoBlock, 2)
lock(&trace.lock)
}
// Write a buffer.
}
// Override.
- if gpstatus == _Gwaiting && gp.waitreason != "" {
- status = gp.waitreason
+ if gpstatus == _Gwaiting && gp.waitreason != waitReasonZero {
+ status = gp.waitreason.String()
}
// approx time the G is blocked, in minutes