mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: prevent mutual deadlock between GC stopTheWorld and suspendG
Almost everywhere we stop the world we casGToWaitingForGC to prevent mutual deadlock with the GC trying to scan our stack. This historically was only necessary if we weren't stopping the world to change the GC phase, because what we were worried about was mutual deadlock with mark workers' use of suspendG. And, they were the only users of suspendG. In Go 1.22 this changed. The execution tracer began using suspendG, too. This leads to the possibility of mutual deadlock between the execution tracer and a goroutine trying to start or end the GC mark phase. The fix is simple: make the stop-the-world calls for the GC also call casGToWaitingForGC. This way, suspendG is guaranteed to make progress in this circumstance, and once it completes, the stop-the-world can complete as well. We can take this a step further, though, and move casGToWaitingForGC into stopTheWorldWithSema, since there's no longer really a place we can afford to skip this detail. While we're here, rename casGToWaitingForGC to casGToWaitingForSuspendG, since the GC is now not the only potential source of mutual deadlock. Fixes #72740. Change-Id: I5e3739a463ef3e8173ad33c531e696e46260692f Reviewed-on: https://go-review.googlesource.com/c/go/+/681501 Reviewed-by: Carlos Amedee <carlos@golang.org> Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
parent
53af292aed
commit
c6ac736288
7 changed files with 65 additions and 45 deletions
|
|
@ -1212,14 +1212,14 @@ func isShrinkStackSafe(gp *g) bool {
|
|||
return false
|
||||
}
|
||||
// We also can't copy the stack while tracing is enabled, and
|
||||
// gp is in _Gwaiting solely to make itself available to the GC.
|
||||
// gp is in _Gwaiting solely to make itself available to suspendG.
|
||||
// In these cases, the G is actually executing on the system
|
||||
// stack, and the execution tracer may want to take a stack trace
|
||||
// of the G's stack. Note: it's safe to access gp.waitreason here.
|
||||
// We're only checking if this is true if we took ownership of the
|
||||
// G with the _Gscan bit. This prevents the goroutine from transitioning,
|
||||
// which prevents gp.waitreason from changing.
|
||||
if traceEnabled() && readgstatus(gp)&^_Gscan == _Gwaiting && gp.waitreason.isWaitingForGC() {
|
||||
if traceEnabled() && readgstatus(gp)&^_Gscan == _Gwaiting && gp.waitreason.isWaitingForSuspendG() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue