aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2015-10-23 15:55:03 -0400
committerAustin Clements <austin@google.com>2015-11-05 21:23:27 +0000
commita51905fa04fafaa8284d5a4585a81da249f9d8fd (patch)
tree4e91efe48ea0f0d7778b4e5507cbb76673c581c7 /src/runtime
parent9630c47e8c86a3fabc008eab9f239480b1d4f8e5 (diff)
downloadgo-a51905fa04fafaa8284d5a4585a81da249f9d8fd.tar.xz
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/16357 Reviewed-by: Rick Hudson <rlh@golang.org> Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/malloc.go2
-rw-r--r--src/runtime/mgc.go70
-rw-r--r--src/runtime/trace.go2
3 files changed, 38 insertions, 36 deletions
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index 230849609f..45ebe712ba 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -737,7 +737,7 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
if shouldhelpgc && gcShouldStart(false) {
gcStart(gcBackgroundMode, false)
} 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
// down this G's allocation and help the GC stay
// scheduled by yielding.
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 4bbd2d02ba..7a4f6f53ea 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -915,31 +915,23 @@ const (
)
// 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
// finish the transition to the decentralized state machine.
-func startGCCoordinator(mode gcMode) {
- if mode != gcBackgroundMode {
- // special synchronous cases
- gc(mode)
- return
- }
-
+func startGCCoordinator() {
// trigger concurrent GC
readied := false
lock(&bggc.lock)
if !bggc.started {
bggc.working = 1
bggc.started = true
+ bggc.wakeSema = 1
readied = true
go backgroundgc()
- } else if bggc.working == 0 {
- bggc.working = 1
+ } else {
+ bggc.working++
readied = true
- ready(bggc.g, 0)
+ semrelease(&bggc.wakeSema)
}
unlock(&bggc.lock)
if readied {
@@ -952,20 +944,21 @@ func startGCCoordinator(mode gcMode) {
// State of the background concurrent GC goroutine.
var bggc struct {
lock mutex
- g *g
working uint
started bool
+
+ wakeSema uint32
}
// backgroundgc is running in a goroutine and does the concurrent GC work.
// bggc holds the state of the backgroundgc.
func backgroundgc() {
- bggc.g = getg()
for {
gc(gcBackgroundMode)
lock(&bggc.lock)
- bggc.working = 0
- goparkunlock(&bggc.lock, "Concurrent GC wait", traceEvGoBlock, 1)
+ bggc.working--
+ unlock(&bggc.lock)
+ semacquire(&bggc.wakeSema, false)
}
}
@@ -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
semacquire(&worldsema, false)
@@ -1135,10 +1118,33 @@ func gc(mode gcMode) {
gcController.assistStartTime = now
work.tMark = now
- // Enable background mark workers and wait for
- // background mark completion.
+ // Enable background mark workers.
gcController.bgMarkStartTime = now
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()
gcMarkRootCheck()
@@ -1170,7 +1176,7 @@ func gc(mode gcMode) {
work.bgMark2.wait()
// Begin mark termination.
- now = nanotime()
+ now := nanotime()
work.tMarkTerm = now
work.pauseStart = now
systemstack(stopTheWorldWithSema)
@@ -1192,10 +1198,6 @@ func gc(mode gcMode) {
gcWakeAllAssists()
gcController.endCycle()
- } else {
- t := nanotime()
- work.tMark, work.tMarkTerm = t, t
- work.heapGoal = work.heap0
}
// World is stopped.
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 06bdf970ec..7ea4e8a61f 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -773,7 +773,7 @@ func traceProcStop(pp *p) {
}
func traceGCStart() {
- traceEvent(traceEvGCStart, 5)
+ traceEvent(traceEvGCStart, 3)
}
func traceGCDone() {