Skip to content

Commit 1303957

Browse files
aclementsrsc
authored andcommitted
runtime: enable write barriers during concurrent scan
Currently, write barriers are only enabled after completion of the concurrent scan phase, as we enter the concurrent mark phase. However, stack barriers are installed during the scan phase and assume that write barriers will track changes to frames above the stack barriers. Since write barriers aren't enabled until after stack barriers are installed, we may miss modifications to the stack that happen after installing the stack barriers and before enabling write barriers. Fix this by enabling write barriers during the scan phase. This commit intentionally makes the minimal change to do this (there's only one line of code change; the rest are comment changes). At the very least, we should consider eliminating the ragged barrier that's intended to synchronize the enabling of write barriers, but now just wastes time. I've included a large comment about extensions and alternative designs. Change-Id: Ib20fede794e4fcb91ddf36f99bd97344d7f96421 Reviewed-on: https://go-review.googlesource.com/10795 Reviewed-by: Russ Cox <rsc@golang.org>
1 parent 6f6403e commit 1303957

File tree

1 file changed

+39
-7
lines changed

1 file changed

+39
-7
lines changed

src/runtime/mgc.go

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,18 @@
6464
// Once all the P's are aware of the new phase they will scan gs on preemption.
6565
// This means that the scanning of preempted gs can't start until all the Ps
6666
// have acknowledged.
67+
// When a stack is scanned, this phase also installs stack barriers to
68+
// track how much of the stack has been active.
69+
// This transition enables write barriers because stack barriers
70+
// assume that writes to higher frames will be tracked by write
71+
// barriers. Technically this only needs write barriers for writes
72+
// to stack slots, but we enable write barriers in general.
6773
// GCscan to GCmark
68-
// GCMark turns on the write barrier which also only greys objects. No scanning
69-
// of objects (making them black) can happen until all the Ps have acknowledged
70-
// the phase change.
74+
// In GCmark, work buffers are drained until there are no more
75+
// pointers to scan.
76+
// No scanning of objects (making them black) can happen until all
77+
// Ps have enabled the write barrier, but that already happened in
78+
// the transition to GCscan.
7179
// GCmark to GCmarktermination
7280
// The only change here is that we start allocating black so the Ps must acknowledge
7381
// the change before we begin the termination algorithm
@@ -220,7 +228,7 @@ var gcBlackenEnabled uint32
220228
const (
221229
_GCoff = iota // GC not running, write barrier disabled
222230
_GCstw // unused state
223-
_GCscan // GC collecting roots into workbufs, write barrier disabled
231+
_GCscan // GC collecting roots into workbufs, write barrier ENABLED
224232
_GCmark // GC marking from workbufs, write barrier ENABLED
225233
_GCmarktermination // GC mark termination: allocate black, P's help GC, write barrier ENABLED
226234
_GCsweep // GC mark completed; sweeping in background, write barrier disabled
@@ -229,7 +237,7 @@ const (
229237
//go:nosplit
230238
func setGCPhase(x uint32) {
231239
atomicstore(&gcphase, x)
232-
writeBarrierEnabled = gcphase == _GCmark || gcphase == _GCmarktermination
240+
writeBarrierEnabled = gcphase == _GCmark || gcphase == _GCmarktermination || gcphase == _GCscan
233241
}
234242

235243
// gcMarkWorkerMode represents the mode that a concurrent mark worker
@@ -833,6 +841,31 @@ func gc(mode int) {
833841
heapGoal = gcController.heapGoal
834842

835843
systemstack(func() {
844+
// Enter scan phase. This enables write
845+
// barriers to track changes to stack frames
846+
// above the stack barrier.
847+
//
848+
// TODO: This has evolved to the point where
849+
// we carefully ensure invariants we no longer
850+
// depend on. Either:
851+
//
852+
// 1) Enable full write barriers for the scan,
853+
// but eliminate the ragged barrier below
854+
// (since the start the world ensures all Ps
855+
// have observed the write barrier enable) and
856+
// consider draining during the scan.
857+
//
858+
// 2) Only enable write barriers for writes to
859+
// the stack at this point, and then enable
860+
// write barriers for heap writes when we
861+
// enter the mark phase. This means we cannot
862+
// drain in the scan phase and must perform a
863+
// ragged barrier to ensure all Ps have
864+
// enabled heap write barriers before we drain
865+
// or enable assists.
866+
//
867+
// 3) Don't install stack barriers over frame
868+
// boundaries where there are up-pointers.
836869
setGCPhase(_GCscan)
837870

838871
// Concurrent scan.
@@ -842,8 +875,7 @@ func gc(mode int) {
842875
}
843876
gcscan_m()
844877

845-
// Enter mark phase. This enables write
846-
// barriers.
878+
// Enter mark phase.
847879
if debug.gctrace > 0 {
848880
tInstallWB = nanotime()
849881
}

0 commit comments

Comments
 (0)