Commit a51905fa authored by Austin Clements's avatar Austin Clements

runtime: decentralize sweep termination and mark transition

This moves all of GC initialization, sweep termination, and the
transition to concurrent marking in to the off->mark transition
function. This means it's now handled on the goroutine that detected
the state exit condition.

As a result, malloc no longer needs to Gosched() at the beginning of
the GC cycle to prevent over-allocation while the GC is starting up
because it will now *help* the GC to start up. The Gosched hack is
still necessary during GC shutdown (this is easy to test by enabling
gctrace and hitting Ctrl-S to block the gctrace output).

At this point, the GC coordinator still handles later phases. This
requires a small tweak to how we start the GC coordinator. Currently,
starting the GC coordinator is best-effort and may fail if the
coordinator is about to park from the previous cycle but hasn't yet.
We fix this by replacing the park/ready to wake up the coordinator
with a semaphore. This is temporary since the coordinator will be
going away in a few commits.

Updates #11970.

Change-Id: I2c6a11c91e72dfbc59c2d8e7c66146dee9a444fe
Reviewed-on: https://go-review.googlesource.com/16357Reviewed-by: default avatarRick Hudson <rlh@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 9630c47e
...@@ -737,7 +737,7 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer { ...@@ -737,7 +737,7 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
if shouldhelpgc && gcShouldStart(false) { if shouldhelpgc && gcShouldStart(false) {
gcStart(gcBackgroundMode, false) gcStart(gcBackgroundMode, false)
} else if shouldhelpgc && bggc.working != 0 && gcBlackenEnabled == 0 { } else if shouldhelpgc && bggc.working != 0 && gcBlackenEnabled == 0 {
// The GC is starting up or shutting down, so we can't // The GC shutting down, so we can't
// assist, but we also can't allocate unabated. Slow // assist, but we also can't allocate unabated. Slow
// down this G's allocation and help the GC stay // down this G's allocation and help the GC stay
// scheduled by yielding. // scheduled by yielding.
......
...@@ -915,31 +915,23 @@ const ( ...@@ -915,31 +915,23 @@ const (
) )
// startGCCoordinator starts and readies the GC coordinator goroutine. // startGCCoordinator starts and readies the GC coordinator goroutine.
// If mode is gcBackgroundMode, this will
// start GC in the background and return. Otherwise, this will block
// until the new GC cycle is started and finishes.
// //
// TODO(austin): This function is temporary and will go away when we // TODO(austin): This function is temporary and will go away when we
// finish the transition to the decentralized state machine. // finish the transition to the decentralized state machine.
func startGCCoordinator(mode gcMode) { func startGCCoordinator() {
if mode != gcBackgroundMode {
// special synchronous cases
gc(mode)
return
}
// trigger concurrent GC // trigger concurrent GC
readied := false readied := false
lock(&bggc.lock) lock(&bggc.lock)
if !bggc.started { if !bggc.started {
bggc.working = 1 bggc.working = 1
bggc.started = true bggc.started = true
bggc.wakeSema = 1
readied = true readied = true
go backgroundgc() go backgroundgc()
} else if bggc.working == 0 { } else {
bggc.working = 1 bggc.working++
readied = true readied = true
ready(bggc.g, 0) semrelease(&bggc.wakeSema)
} }
unlock(&bggc.lock) unlock(&bggc.lock)
if readied { if readied {
...@@ -952,20 +944,21 @@ func startGCCoordinator(mode gcMode) { ...@@ -952,20 +944,21 @@ func startGCCoordinator(mode gcMode) {
// State of the background concurrent GC goroutine. // State of the background concurrent GC goroutine.
var bggc struct { var bggc struct {
lock mutex lock mutex
g *g
working uint working uint
started bool started bool
wakeSema uint32
} }
// backgroundgc is running in a goroutine and does the concurrent GC work. // backgroundgc is running in a goroutine and does the concurrent GC work.
// bggc holds the state of the backgroundgc. // bggc holds the state of the backgroundgc.
func backgroundgc() { func backgroundgc() {
bggc.g = getg()
for { for {
gc(gcBackgroundMode) gc(gcBackgroundMode)
lock(&bggc.lock) lock(&bggc.lock)
bggc.working = 0 bggc.working--
goparkunlock(&bggc.lock, "Concurrent GC wait", traceEvGoBlock, 1) unlock(&bggc.lock)
semacquire(&bggc.wakeSema, false)
} }
} }
...@@ -1042,16 +1035,6 @@ func gcStart(mode gcMode, forceTrigger bool) { ...@@ -1042,16 +1035,6 @@ func gcStart(mode gcMode, forceTrigger bool) {
} }
} }
// TODO: Move sweep termination and initialization from the
// coordinator to here.
startGCCoordinator(mode)
if useStartSema {
semrelease(&work.startSema)
}
}
func gc(mode gcMode) {
// Ok, we're doing it! Stop everybody else // Ok, we're doing it! Stop everybody else
semacquire(&worldsema, false) semacquire(&worldsema, false)
...@@ -1135,10 +1118,33 @@ func gc(mode gcMode) { ...@@ -1135,10 +1118,33 @@ func gc(mode gcMode) {
gcController.assistStartTime = now gcController.assistStartTime = now
work.tMark = now work.tMark = now
// Enable background mark workers and wait for // Enable background mark workers.
// background mark completion.
gcController.bgMarkStartTime = now gcController.bgMarkStartTime = now
work.bgMark1.clear() work.bgMark1.clear()
// TODO: Make mark 1 completion handle the transition.
startGCCoordinator()
} else {
t := nanotime()
work.tMark, work.tMarkTerm = t, t
work.heapGoal = work.heap0
// Perform mark termination. This will restart the world.
gc(mode)
}
if useStartSema {
semrelease(&work.startSema)
}
}
func gc(mode gcMode) {
// If mode == gcBackgroundMode, world is not stopped.
// If mode != gcBackgroundMode, world is stopped.
// TODO(austin): This is temporary.
if mode == gcBackgroundMode {
// Wait for background mark completion.
work.bgMark1.wait() work.bgMark1.wait()
gcMarkRootCheck() gcMarkRootCheck()
...@@ -1170,7 +1176,7 @@ func gc(mode gcMode) { ...@@ -1170,7 +1176,7 @@ func gc(mode gcMode) {
work.bgMark2.wait() work.bgMark2.wait()
// Begin mark termination. // Begin mark termination.
now = nanotime() now := nanotime()
work.tMarkTerm = now work.tMarkTerm = now
work.pauseStart = now work.pauseStart = now
systemstack(stopTheWorldWithSema) systemstack(stopTheWorldWithSema)
...@@ -1192,10 +1198,6 @@ func gc(mode gcMode) { ...@@ -1192,10 +1198,6 @@ func gc(mode gcMode) {
gcWakeAllAssists() gcWakeAllAssists()
gcController.endCycle() gcController.endCycle()
} else {
t := nanotime()
work.tMark, work.tMarkTerm = t, t
work.heapGoal = work.heap0
} }
// World is stopped. // World is stopped.
......
...@@ -773,7 +773,7 @@ func traceProcStop(pp *p) { ...@@ -773,7 +773,7 @@ func traceProcStop(pp *p) {
} }
func traceGCStart() { func traceGCStart() {
traceEvent(traceEvGCStart, 5) traceEvent(traceEvGCStart, 3)
} }
func traceGCDone() { func traceGCDone() {
......
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