Skip to content

Commit 6508fda

Browse files
committed
runtime: formalize and fix gcPercent synchronization
Currently gcController.gcPercent is read non-atomically by gcControllerState.revise and gcTrigger.test, but these users may execute concurrently with an update to gcPercent. Although revise's results are best-effort, reading it directly in this way is, generally speaking, unsafe. This change makes gcPercent atomically updated for concurrent readers and documents the complete synchronization semantics. Because gcPercent otherwise only updated with the heap lock held or the world stopped, all other reads can remain unsynchronized. For #44167. Change-Id: If09af103aae84a1e133e2d4fed8ab888d4b8f457 Reviewed-on: https://go-review.googlesource.com/c/go/+/308690 Trust: Michael Knyszek <[email protected]> Run-TryBot: Michael Knyszek <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent 8151b56 commit 6508fda

File tree

2 files changed

+8
-3
lines changed

2 files changed

+8
-3
lines changed

src/runtime/mgc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ func (t gcTrigger) test() bool {
545545
// own write.
546546
return gcController.heapLive >= gcController.trigger
547547
case gcTriggerTime:
548-
if gcController.gcPercent < 0 {
548+
if atomic.Loadint32(&gcController.gcPercent) < 0 {
549549
return false
550550
}
551551
lastgc := int64(atomic.Load64(&memstats.last_gc_nanotime))

src/runtime/mgcpacer.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ var gcController gcControllerState
7373

7474
type gcControllerState struct {
7575
// Initialized from $GOGC. GOGC=off means no GC.
76+
//
77+
// Updated atomically with mheap_.lock held or during a STW.
78+
// Safe to read atomically at any time, or non-atomically with
79+
// mheap_.lock or STW.
7680
gcPercent int32
7781

7882
_ uint32 // padding so following 64-bit values are 8-byte aligned
@@ -355,7 +359,7 @@ func (c *gcControllerState) startCycle() {
355359
// is when assists are enabled and the necessary statistics are
356360
// available).
357361
func (c *gcControllerState) revise() {
358-
gcPercent := c.gcPercent
362+
gcPercent := atomic.Loadint32(&c.gcPercent)
359363
if gcPercent < 0 {
360364
// If GC is disabled but we're running a forced GC,
361365
// act like GOGC is huge for the below calculations.
@@ -800,7 +804,8 @@ func (c *gcControllerState) setGCPercent(in int32) int32 {
800804
if in < 0 {
801805
in = -1
802806
}
803-
c.gcPercent = in
807+
// Write it atomically so readers like revise() can read it safely.
808+
atomic.Storeint32(&c.gcPercent, in)
804809
c.heapMinimum = defaultHeapMinimum * uint64(c.gcPercent) / 100
805810
// Update pacing in response to gcPercent change.
806811
c.commit(c.triggerRatio)

0 commit comments

Comments
 (0)