Skip to content

Commit dbade77

Browse files
committed
runtime: refactor findrunnable spinning recheck
Break the main components of the findrunnable spinning -> non-spinning recheck out into their own functions, which simplifies both findrunnable and the new functions, which can make use of fancy features like early returns. This CL should have no functional changes. For #43997 For #44313 Change-Id: I6d3060fcecda9920a3471ff338f73d53b1d848a3 Reviewed-on: https://go-review.googlesource.com/c/go/+/307914 Trust: Michael Pratt <[email protected]> Run-TryBot: Michael Pratt <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent 7473a6a commit dbade77

File tree

1 file changed

+110
-67
lines changed

1 file changed

+110
-67
lines changed

src/runtime/proc.go

+110-67
Original file line numberDiff line numberDiff line change
@@ -2799,86 +2799,42 @@ top:
27992799
}
28002800
}
28012801

2802-
// check all runqueues once again
2803-
for id, _p_ := range allpSnapshot {
2804-
if !idlepMaskSnapshot.read(uint32(id)) && !runqempty(_p_) {
2805-
lock(&sched.lock)
2806-
_p_ = pidleget()
2807-
unlock(&sched.lock)
2808-
if _p_ != nil {
2809-
acquirep(_p_)
2810-
if wasSpinning {
2811-
_g_.m.spinning = true
2812-
atomic.Xadd(&sched.nmspinning, 1)
2813-
}
2814-
goto top
2815-
}
2816-
break
2802+
// Check all runqueues once again.
2803+
_p_ = checkRunqsNoP(allpSnapshot, idlepMaskSnapshot)
2804+
if _p_ != nil {
2805+
acquirep(_p_)
2806+
if wasSpinning {
2807+
_g_.m.spinning = true
2808+
atomic.Xadd(&sched.nmspinning, 1)
28172809
}
2810+
goto top
28182811
}
28192812

28202813
// Similar to above, check for timer creation or expiry concurrently with
28212814
// transitioning from spinning to non-spinning. Note that we cannot use
28222815
// checkTimers here because it calls adjusttimers which may need to allocate
28232816
// memory, and that isn't allowed when we don't have an active P.
2824-
for id, _p_ := range allpSnapshot {
2825-
if timerpMaskSnapshot.read(uint32(id)) {
2826-
w := nobarrierWakeTime(_p_)
2827-
if w != 0 && (pollUntil == 0 || w < pollUntil) {
2828-
pollUntil = w
2829-
}
2830-
}
2831-
}
2817+
pollUntil = checkTimersNoP(allpSnapshot, timerpMaskSnapshot, pollUntil)
28322818

2833-
// Check for idle-priority GC work again.
2834-
//
2835-
// N.B. Since we have no P, gcBlackenEnabled may change at any time; we
2836-
// must check again after acquiring a P.
2837-
if atomic.Load(&gcBlackenEnabled) != 0 && gcMarkWorkAvailable(nil) {
2838-
// Work is available; we can start an idle GC worker only if
2839-
// there is an available P and available worker G.
2840-
//
2841-
// We can attempt to acquire these in either order. Workers are
2842-
// almost always available (see comment in findRunnableGCWorker
2843-
// for the one case there may be none). Since we're slightly
2844-
// less likely to find a P, check for that first.
2845-
lock(&sched.lock)
2846-
var node *gcBgMarkWorkerNode
2847-
_p_ = pidleget()
2848-
if _p_ != nil {
2849-
// Now that we own a P, gcBlackenEnabled can't change
2850-
// (as it requires STW).
2851-
if gcBlackenEnabled != 0 {
2852-
node = (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop())
2853-
if node == nil {
2854-
pidleput(_p_)
2855-
_p_ = nil
2856-
}
2857-
} else {
2858-
pidleput(_p_)
2859-
_p_ = nil
2860-
}
2819+
// Finally, check for idle-priority GC work.
2820+
_p_, gp = checkIdleGCNoP()
2821+
if _p_ != nil {
2822+
acquirep(_p_)
2823+
if wasSpinning {
2824+
_g_.m.spinning = true
2825+
atomic.Xadd(&sched.nmspinning, 1)
28612826
}
2862-
unlock(&sched.lock)
2863-
if _p_ != nil {
2864-
acquirep(_p_)
2865-
if wasSpinning {
2866-
_g_.m.spinning = true
2867-
atomic.Xadd(&sched.nmspinning, 1)
2868-
}
28692827

2870-
// Run the idle worker.
2871-
_p_.gcMarkWorkerMode = gcMarkWorkerIdleMode
2872-
gp := node.gp.ptr()
2873-
casgstatus(gp, _Gwaiting, _Grunnable)
2874-
if trace.enabled {
2875-
traceGoUnpark(gp, 0)
2876-
}
2877-
return gp, false
2828+
// Run the idle worker.
2829+
_p_.gcMarkWorkerMode = gcMarkWorkerIdleMode
2830+
casgstatus(gp, _Gwaiting, _Grunnable)
2831+
if trace.enabled {
2832+
traceGoUnpark(gp, 0)
28782833
}
2834+
return gp, false
28792835
}
28802836

2881-
// poll network
2837+
// Poll network until next timer.
28822838
if netpollinited() && (atomic.Load(&netpollWaiters) > 0 || pollUntil != 0) && atomic.Xchg64(&sched.lastpoll, 0) != 0 {
28832839
atomic.Store64(&sched.pollUntil, uint64(pollUntil))
28842840
if _g_.m.p != 0 {
@@ -3038,6 +2994,93 @@ func stealWork(now int64) (gp *g, inheritTime bool, rnow, pollUntil int64, newWo
30382994
return nil, false, now, pollUntil, ranTimer
30392995
}
30402996

2997+
// Check all Ps for a runnable G to steal.
2998+
//
2999+
// On entry we have no P. If a G is available to steal and a P is available,
3000+
// the P is returned which the caller should acquire and attempt to steal the
3001+
// work to.
3002+
func checkRunqsNoP(allpSnapshot []*p, idlepMaskSnapshot pMask) *p {
3003+
for id, p2 := range allpSnapshot {
3004+
if !idlepMaskSnapshot.read(uint32(id)) && !runqempty(p2) {
3005+
lock(&sched.lock)
3006+
pp := pidleget()
3007+
unlock(&sched.lock)
3008+
if pp != nil {
3009+
return pp
3010+
}
3011+
3012+
// Can't get a P, don't bother checking remaining Ps.
3013+
break
3014+
}
3015+
}
3016+
3017+
return nil
3018+
}
3019+
3020+
// Check all Ps for a timer expiring sooner than pollUntil.
3021+
//
3022+
// Returns updated pollUntil value.
3023+
func checkTimersNoP(allpSnapshot []*p, timerpMaskSnapshot pMask, pollUntil int64) int64 {
3024+
for id, p2 := range allpSnapshot {
3025+
if timerpMaskSnapshot.read(uint32(id)) {
3026+
w := nobarrierWakeTime(p2)
3027+
if w != 0 && (pollUntil == 0 || w < pollUntil) {
3028+
pollUntil = w
3029+
}
3030+
}
3031+
}
3032+
3033+
return pollUntil
3034+
}
3035+
3036+
// Check for idle-priority GC, without a P on entry.
3037+
//
3038+
// If some GC work, a P, and a worker G are all available, the P and G will be
3039+
// returned. The returned P has not been wired yet.
3040+
func checkIdleGCNoP() (*p, *g) {
3041+
// N.B. Since we have no P, gcBlackenEnabled may change at any time; we
3042+
// must check again after acquiring a P.
3043+
if atomic.Load(&gcBlackenEnabled) == 0 {
3044+
return nil, nil
3045+
}
3046+
if !gcMarkWorkAvailable(nil) {
3047+
return nil, nil
3048+
}
3049+
3050+
// Work is available; we can start an idle GC worker only if
3051+
// there is an available P and available worker G.
3052+
//
3053+
// We can attempt to acquire these in either order. Workers are
3054+
// almost always available (see comment in findRunnableGCWorker
3055+
// for the one case there may be none). Since we're slightly
3056+
// less likely to find a P, check for that first.
3057+
lock(&sched.lock)
3058+
pp := pidleget()
3059+
unlock(&sched.lock)
3060+
if pp == nil {
3061+
return nil, nil
3062+
}
3063+
3064+
// Now that we own a P, gcBlackenEnabled can't change
3065+
// (as it requires STW).
3066+
if gcBlackenEnabled == 0 {
3067+
lock(&sched.lock)
3068+
pidleput(pp)
3069+
unlock(&sched.lock)
3070+
return nil, nil
3071+
}
3072+
3073+
node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop())
3074+
if node == nil {
3075+
lock(&sched.lock)
3076+
pidleput(pp)
3077+
unlock(&sched.lock)
3078+
return nil, nil
3079+
}
3080+
3081+
return pp, node.gp.ptr()
3082+
}
3083+
30413084
// wakeNetPoller wakes up the thread sleeping in the network poller if it isn't
30423085
// going to wake up before the when argument; or it wakes an idle P to service
30433086
// timers and the network poller if there isn't one already.

0 commit comments

Comments
 (0)