From e8337491aa6b6a5f96f31077764352549dc34159 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 11 Mar 2016 17:00:46 -0500 Subject: [PATCH] runtime: free dead G stacks concurrently Currently we free cached stacks of dead Gs during STW stack root marking. We do this during STW because there's no way to take ownership of a particular dead G, so attempting to free a dead G's stack during concurrent stack root marking could race with reusing that G. However, we can do this concurrently if we take a completely different approach. One way to prevent reuse of a dead G is to remove it from the free G list. Hence, this adds a new fixed root marking task that simply removes all Gs from the list of dead Gs with cached stacks, frees their stacks, and then adds them to the list of dead Gs without cached stacks. This is also a necessary step toward rescanning only dirty stacks, since it eliminates another task from STW stack marking. Change-Id: Iefbad03078b284a2e7bf30fba397da4ca87fe095 Reviewed-on: https://go-review.googlesource.com/20665 Reviewed-by: Rick Hudson Run-TryBot: Austin Clements TryBot-Result: Gobot Gobot --- src/runtime/mgcmark.go | 45 +++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index ad64b735a5..3c6aec943b 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -15,6 +15,7 @@ import ( const ( fixedRootFinalizers = iota fixedRootFlushCaches + fixedRootFreeGStacks fixedRootCount // rootBlockBytes is the number of bytes to scan per data or @@ -126,6 +127,13 @@ func markroot(gcw *gcWork, i uint32) { flushallmcaches() } + case i == fixedRootFreeGStacks: + // Only do this once per GC cycle; preferably + // concurrently. + if !work.markrootDone { + markrootFreeGStacks() + } + case baseSpans <= i && i < baseStacks: // mark MSpan.specials markrootSpans(gcw, int(i-baseSpans)) @@ -144,13 +152,6 @@ func markroot(gcw *gcWork, i uint32) { gp.waitsince = work.tstart } - if gcphase == _GCmarktermination && status == _Gdead { - // Free gp's stack if necessary. Only do this - // during mark termination because otherwise - // _Gdead may be transient. - shrinkstack(gp) - } - if gcphase != _GCmarktermination && gp.startpc == gcBgMarkWorkerPC { // GC background workers may be // non-preemptible, so we may deadlock if we @@ -215,6 +216,36 @@ func markrootBlock(b0, n0 uintptr, ptrmask0 *uint8, gcw *gcWork, shard int) { scanblock(b, n, ptrmask, gcw) } +// markrootFreeGStacks frees stacks of dead Gs. +// +// This does not free stacks of dead Gs cached on Ps, but having a few +// cached stacks around isn't a problem. +// +//TODO go:nowritebarrier +func markrootFreeGStacks() { + // Take list of dead Gs with stacks. + lock(&sched.gflock) + list := sched.gfreeStack + sched.gfreeStack = nil + unlock(&sched.gflock) + if list == nil { + return + } + + // Free stacks. + tail := list + for gp := list; gp != nil; gp = gp.schedlink.ptr() { + shrinkstack(gp) + tail = gp + } + + // Put Gs back on the free list. + lock(&sched.gflock) + tail.schedlink.set(sched.gfreeNoStack) + sched.gfreeNoStack = list + unlock(&sched.gflock) +} + // markrootSpans marks roots for one shard of work.spans. // //go:nowritebarrier -- 2.48.1