diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 94a6e00685..8c50e9fb79 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1461,14 +1461,25 @@ func gcBgMarkWorker(_p_ *p) { throw("work.nwait was > work.nproc") } - switch _p_.gcMarkWorkerMode { - default: - throw("gcBgMarkWorker: unexpected gcMarkWorkerMode") - case gcMarkWorkerDedicatedMode: - gcDrain(&_p_.gcw, gcDrainNoBlock|gcDrainFlushBgCredit) - case gcMarkWorkerFractionalMode, gcMarkWorkerIdleMode: - gcDrain(&_p_.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit) - } + systemstack(func() { + // Mark our goroutine preemptible so its stack + // can be scanned. This lets two mark workers + // scan each other (otherwise, they would + // deadlock). We must not modify anything on + // the G stack. However, stack shrinking is + // disabled for mark workers, so it is safe to + // read from the G stack. + casgstatus(gp, _Grunning, _Gwaiting) + switch _p_.gcMarkWorkerMode { + default: + throw("gcBgMarkWorker: unexpected gcMarkWorkerMode") + case gcMarkWorkerDedicatedMode: + gcDrain(&_p_.gcw, gcDrainNoBlock|gcDrainFlushBgCredit) + case gcMarkWorkerFractionalMode, gcMarkWorkerIdleMode: + gcDrain(&_p_.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit) + } + casgstatus(gp, _Gwaiting, _Grunning) + }) // If we are nearing the end of mark, dispose // of the cache promptly. We must do this diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index e62e470217..9489a0a344 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -221,24 +221,13 @@ func markroot(gcw *gcWork, i uint32) { gp.waitsince = work.tstart } - if gcphase != _GCmarktermination && gp.startpc == gcBgMarkWorkerPC && readgstatus(gp) != _Gdead { - // GC background workers may be - // non-preemptible, so we may deadlock if we - // try to scan them during a concurrent phase. - // They also have tiny stacks, so just ignore - // them until mark termination. - gp.gcscandone = true - queueRescan(gp) - break - } - // scang must be done on the system stack in case // we're trying to scan our own stack. systemstack(func() { // If this is a self-scan, put the user G in // _Gwaiting to prevent self-deadlock. It may - // already be in _Gwaiting if this is mark - // termination. + // already be in _Gwaiting if this is a mark + // worker or we're in mark termination. userG := getg().m.curg selfScan := gp == userG && readgstatus(userG) == _Grunning if selfScan { diff --git a/src/runtime/stack.go b/src/runtime/stack.go index e803dc17a0..dfc71b41c3 100644 --- a/src/runtime/stack.go +++ b/src/runtime/stack.go @@ -1122,6 +1122,11 @@ func shrinkstack(gp *g) { if debug.gcshrinkstackoff > 0 { return } + if gp.startpc == gcBgMarkWorkerPC { + // We're not allowed to shrink the gcBgMarkWorker + // stack (see gcBgMarkWorker for explanation). + return + } oldsize := gp.stackAlloc newsize := oldsize / 2