]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: free dead G stacks concurrently
authorAustin Clements <austin@google.com>
Fri, 11 Mar 2016 22:00:46 +0000 (17:00 -0500)
committerAustin Clements <austin@google.com>
Tue, 26 Apr 2016 23:39:55 +0000 (23:39 +0000)
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 <rlh@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/runtime/mgcmark.go

index ad64b735a5f6c318af2f41cd5e54031446d76b8f..3c6aec943b48efab2a6128f0479120782bbbfbc8 100644 (file)
@@ -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