Commit e8337491 authored by Austin Clements's avatar Austin Clements

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/20665Reviewed-by: default avatarRick Hudson <rlh@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 1a2cf91f
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment