Skip to content

Commit bd07faf

Browse files
mknyszekgopherbot
authored andcommitted
runtime: disable stack shrinking for all waiting-for-suspendG cases
Currently isShrinkStackSafe returns false if a goroutine is put into _Gwaiting while it actually goes and executes on the system stack. For a long time, we needed to be robust to the goroutine's stack shrinking while we're executing on the system stack. Unfortunately, this has become harder and harder to do over time. First, the execution tracer might be invoked in these contexts and it may wish to take a stack trace. We cannot take the stack trace if the garbage collector might concurrently shrink the stack of the user goroutine we want to trace. So, isShrinkStackSafe grew the condition that we wouldn't try to shrink the stack in these cases if execution tracing was enabled. Today, runtime.mutex may wish to take a stack trace for the mutex profile, and it can happen in a very similar context. Taking the stack trace is no longer safe. This change takes the stance that we stop trying to make this work at all, and instead guarantee that the stack won't move while we're in these sensitive contexts. Change-Id: Ibfad2d7a335ee97cecaa48001df0db9812deeab1 Reviewed-on: https://go-review.googlesource.com/c/go/+/692716 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Pratt <[email protected]> Auto-Submit: Michael Knyszek <[email protected]>
1 parent a651e2e commit bd07faf

File tree

3 files changed

+27
-42
lines changed

3 files changed

+27
-42
lines changed

src/runtime/mgc.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,18 +1521,15 @@ func gcBgMarkWorker(ready chan struct{}) {
15211521
}
15221522

15231523
systemstack(func() {
1524-
// Mark our goroutine preemptible so its stack
1525-
// can be scanned or observed by the execution
1526-
// tracer. This, for example, lets two mark workers
1527-
// scan each other (otherwise, they would
1528-
// deadlock). We must not modify anything on
1529-
// the G stack. However, stack shrinking is
1530-
// disabled for mark workers, so it is safe to
1531-
// read from the G stack.
1524+
// Mark our goroutine preemptible so its stack can be scanned or observed
1525+
// by the execution tracer. This, for example, lets two mark workers scan
1526+
// each other (otherwise, they would deadlock).
15321527
//
1533-
// N.B. The execution tracer is not aware of this status
1534-
// transition and handles it specially based on the
1535-
// wait reason.
1528+
// casGToWaitingForSuspendG marks the goroutine as ineligible for a
1529+
// stack shrink, effectively pinning the stack in memory for the duration.
1530+
//
1531+
// N.B. The execution tracer is not aware of this status transition and
1532+
// handles it specially based on the wait reason.
15361533
casGToWaitingForSuspendG(gp, _Grunning, waitReasonGCWorkerActive)
15371534
switch pp.gcMarkWorkerMode {
15381535
default:

src/runtime/proc.go

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,9 @@ func casGToWaiting(gp *g, old uint32, reason waitReason) {
13721372
// casGToWaitingForSuspendG transitions gp from old to _Gwaiting, and sets the wait reason.
13731373
// The wait reason must be a valid isWaitingForSuspendG wait reason.
13741374
//
1375+
// While a goroutine is in this state, it's stack is effectively pinned.
1376+
// The garbage collector must not shrink or otherwise mutate the goroutine's stack.
1377+
//
13751378
// Use this over casgstatus when possible to ensure that a waitreason is set.
13761379
func casGToWaitingForSuspendG(gp *g, old uint32, reason waitReason) {
13771380
if !reason.isWaitingForSuspendG() {
@@ -1608,18 +1611,11 @@ func stopTheWorldWithSema(reason stwReason) worldStop {
16081611
// stack while we try to stop the world since otherwise we could get
16091612
// in a mutual preemption deadlock.
16101613
//
1611-
// We must not modify anything on the G stack because a stack shrink
1612-
// may occur, now that we switched to _Gwaiting, specifically if we're
1613-
// doing this during the mark phase (mark termination excepted, since
1614-
// we know that stack scanning is done by that point). A stack shrink
1615-
// is otherwise OK though because in order to return from this function
1616-
// (and to leave the system stack) we must have preempted all
1617-
// goroutines, including any attempting to scan our stack, in which
1618-
// case, any stack shrinking will have already completed by the time we
1619-
// exit.
1614+
// casGToWaitingForSuspendG marks the goroutine as ineligible for a
1615+
// stack shrink, effectively pinning the stack in memory for the duration.
16201616
//
16211617
// N.B. The execution tracer is not aware of this status transition and
1622-
// andles it specially based on the wait reason.
1618+
// handles it specially based on the wait reason.
16231619
casGToWaitingForSuspendG(getg().m.curg, _Grunning, waitReasonStoppingTheWorld)
16241620

16251621
trace := traceAcquire()
@@ -2106,16 +2102,11 @@ func forEachP(reason waitReason, fn func(*p)) {
21062102
// deadlock as we attempt to preempt a goroutine that's trying
21072103
// to preempt us (e.g. for a stack scan).
21082104
//
2109-
// We must not modify anything on the G stack because a stack shrink
2110-
// may occur. A stack shrink is otherwise OK though because in order
2111-
// to return from this function (and to leave the system stack) we
2112-
// must have preempted all goroutines, including any attempting
2113-
// to scan our stack, in which case, any stack shrinking will
2114-
// have already completed by the time we exit.
2105+
// casGToWaitingForSuspendG marks the goroutine as ineligible for a
2106+
// stack shrink, effectively pinning the stack in memory for the duration.
21152107
//
2116-
// N.B. The execution tracer is not aware of this status
2117-
// transition and handles it specially based on the
2118-
// wait reason.
2108+
// N.B. The execution tracer is not aware of this status transition and
2109+
// handles it specially based on the wait reason.
21192110
casGToWaitingForSuspendG(gp, _Grunning, reason)
21202111
forEachPInternal(fn)
21212112
casgstatus(gp, _Gwaiting, _Grunning)

src/runtime/stack.go

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,15 +1214,18 @@ func isShrinkStackSafe(gp *g) bool {
12141214
if gp.parkingOnChan.Load() {
12151215
return false
12161216
}
1217-
// We also can't copy the stack while tracing is enabled, and
1218-
// gp is in _Gwaiting solely to make itself available to suspendG.
1217+
// We also can't copy the stack while a gp is in _Gwaiting solely
1218+
// to make itself available to suspendG.
1219+
//
12191220
// In these cases, the G is actually executing on the system
1220-
// stack, and the execution tracer may want to take a stack trace
1221-
// of the G's stack. Note: it's safe to access gp.waitreason here.
1222-
// We're only checking if this is true if we took ownership of the
1221+
// stack, and the execution tracer, mutex profiler, etc. may want
1222+
// to take a stack trace of the G's stack.
1223+
//
1224+
// Note: it's safe to access gp.waitreason here.
1225+
// We're only calling isShrinkStackSafe if we took ownership of the
12231226
// G with the _Gscan bit. This prevents the goroutine from transitioning,
12241227
// which prevents gp.waitreason from changing.
1225-
if traceEnabled() && readgstatus(gp)&^_Gscan == _Gwaiting && gp.waitreason.isWaitingForSuspendG() {
1228+
if readgstatus(gp)&^_Gscan == _Gwaiting && gp.waitreason.isWaitingForSuspendG() {
12261229
return false
12271230
}
12281231
return true
@@ -1258,12 +1261,6 @@ func shrinkstack(gp *g) {
12581261
if debug.gcshrinkstackoff > 0 {
12591262
return
12601263
}
1261-
f := findfunc(gp.startpc)
1262-
if f.valid() && f.funcID == abi.FuncID_gcBgMarkWorker {
1263-
// We're not allowed to shrink the gcBgMarkWorker
1264-
// stack (see gcBgMarkWorker for explanation).
1265-
return
1266-
}
12671264

12681265
oldsize := gp.stack.hi - gp.stack.lo
12691266
newsize := oldsize / 2

0 commit comments

Comments
 (0)