diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 2cc29df4346..5145c84aea8 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -721,9 +721,13 @@ func casgstatus(gp *g, oldval, newval uint32) { throw("casgstatus") } + // See http://golang.org/cl/21503 for justification of the yield delay. + const yieldDelay = 5 * 1000 + var nextYield int64 + // loop if gp->atomicstatus is in a scan state giving // GC time to finish and change the state to oldval. - for !atomic.Cas(&gp.atomicstatus, oldval, newval) { + for i := 0; !atomic.Cas(&gp.atomicstatus, oldval, newval); i++ { if oldval == _Gwaiting && gp.atomicstatus == _Grunnable { systemstack(func() { throw("casgstatus: waiting for Gwaiting but is Grunnable") @@ -736,6 +740,18 @@ func casgstatus(gp *g, oldval, newval uint32) { // gcphasework(gp) // }) // } + // But meanwhile just yield. + if i == 0 { + nextYield = nanotime() + yieldDelay + } + if nanotime() < nextYield { + for x := 0; x < 10 && gp.atomicstatus != oldval; x++ { + procyield(1) + } + } else { + osyield() + nextYield = nanotime() + yieldDelay/2 + } } if newval == _Grunning { gp.gcscanvalid = false @@ -773,12 +789,17 @@ func scang(gp *g) { gp.gcscandone = false + // See http://golang.org/cl/21503 for justification of the yield delay. + const yieldDelay = 10 * 1000 + var nextYield int64 + // Endeavor to get gcscandone set to true, // either by doing the stack scan ourselves or by coercing gp to scan itself. // gp.gcscandone can transition from false to true when we're not looking // (if we asked for preemption), so any time we lock the status using // castogscanstatus we have to double-check that the scan is still not done. - for !gp.gcscandone { +loop: + for i := 0; !gp.gcscandone; i++ { switch s := readgstatus(gp); s { default: dumpgstatus(gp) @@ -787,6 +808,7 @@ func scang(gp *g) { case _Gdead: // No stack. gp.gcscandone = true + break loop case _Gcopystack: // Stack being switched. Go around again. @@ -802,6 +824,7 @@ func scang(gp *g) { gp.gcscandone = true } restartg(gp) + break loop } case _Gscanwaiting: @@ -827,6 +850,16 @@ func scang(gp *g) { casfrom_Gscanstatus(gp, _Gscanrunning, _Grunning) } } + + if i == 0 { + nextYield = nanotime() + yieldDelay + } + if nanotime() < nextYield { + procyield(10) + } else { + osyield() + nextYield = nanotime() + yieldDelay/2 + } } gp.preemptscan = false // cancel scan request if no longer needed