Skip to content

Commit

Permalink
runtime: remove g.gcscanvalid
Browse files Browse the repository at this point in the history
Currently, gcscanvalid is used to resolve a race between attempts to
scan a stack. Now that there's a clear owner of the stack scan
operation, there's no longer any danger of racing or attempting to
scan a stack more than once, so this CL eliminates gcscanvalid.

I double-checked my reasoning by first adding a throw if gcscanvalid
was set in scanstack and verifying that all.bash still passed.

For #10958, #24543.
Fixes #24363.

Change-Id: I76794a5fcda325ed7cfc2b545e2a839b8b3bc713
Reviewed-on: https://go-review.googlesource.com/c/go/+/201139
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
  • Loading branch information
aclements committed Oct 25, 2019
1 parent 1b79afe commit 8c58615
Show file tree
Hide file tree
Showing 5 changed files with 3 additions and 43 deletions.
3 changes: 1 addition & 2 deletions src/runtime/mgc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2168,8 +2168,7 @@ func gcResetMarkState() {
// allgs doesn't change.
lock(&allglock)
for _, gp := range allgs {
gp.gcscandone = false // set to true in gcphasework
gp.gcscanvalid = false // stack has not been scanned
gp.gcscandone = false // set to true in gcphasework
gp.gcAssistBytes = 0
}
unlock(&allglock)
Expand Down
9 changes: 1 addition & 8 deletions src/runtime/mgcmark.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ func gcMarkRootCheck() {
fail:
println("gp", gp, "goid", gp.goid,
"status", readgstatus(gp),
"gcscandone", gp.gcscandone,
"gcscanvalid", gp.gcscanvalid)
"gcscandone", gp.gcscandone)
unlock(&allglock) // Avoid self-deadlock with traceback.
throw("scan missed a g")
}
Expand Down Expand Up @@ -674,10 +673,6 @@ func gcFlushBgCredit(scanWork int64) {
//go:nowritebarrier
//go:systemstack
func scanstack(gp *g, gcw *gcWork) {
if gp.gcscanvalid {
return
}

if readgstatus(gp)&_Gscan == 0 {
print("runtime:scanstack: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", hex(readgstatus(gp)), "\n")
throw("scanstack - bad status")
Expand Down Expand Up @@ -817,8 +812,6 @@ func scanstack(gp *g, gcw *gcWork) {
if state.buf != nil || state.freeBuf != nil {
throw("remaining pointer buffers")
}

gp.gcscanvalid = true
}

// Scan a stack frame: local variables and function arguments/results.
Expand Down
31 changes: 0 additions & 31 deletions src/runtime/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -710,18 +710,6 @@ func readgstatus(gp *g) uint32 {
return atomic.Load(&gp.atomicstatus)
}

// Ownership of gcscanvalid:
//
// If gp is running (meaning status == _Grunning or _Grunning|_Gscan),
// then gp owns gp.gcscanvalid, and other goroutines must not modify it.
//
// Otherwise, a second goroutine can lock the scan state by setting _Gscan
// in the status bit and then modify gcscanvalid, and then unlock the scan state.
//
// Note that the first condition implies an exception to the second:
// if a second goroutine changes gp's status to _Grunning|_Gscan,
// that second goroutine still does not have the right to modify gcscanvalid.

// The Gscanstatuses are acting like locks and this releases them.
// If it proves to be a performance hit we should be able to make these
// simple atomic stores but for now we are going to throw if
Expand Down Expand Up @@ -781,17 +769,6 @@ func casgstatus(gp *g, oldval, newval uint32) {
})
}

if oldval == _Grunning && gp.gcscanvalid {
// If oldvall == _Grunning, then the actual status must be
// _Grunning or _Grunning|_Gscan; either way,
// we own gp.gcscanvalid, so it's safe to read.
// gp.gcscanvalid must not be true when we are running.
systemstack(func() {
print("runtime: casgstatus ", hex(oldval), "->", hex(newval), " gp.status=", hex(gp.atomicstatus), " gp.gcscanvalid=true\n")
throw("casgstatus")
})
}

// See https://golang.org/cl/21503 for justification of the yield delay.
const yieldDelay = 5 * 1000
var nextYield int64
Expand All @@ -814,9 +791,6 @@ func casgstatus(gp *g, oldval, newval uint32) {
nextYield = nanotime() + yieldDelay/2
}
}
if newval == _Grunning {
gp.gcscanvalid = false
}
}

// casgstatus(gp, oldstatus, Gcopystack), assuming oldstatus is Gwaiting or Grunnable.
Expand Down Expand Up @@ -1585,7 +1559,6 @@ func oneNewExtraM() {
gp.syscallpc = gp.sched.pc
gp.syscallsp = gp.sched.sp
gp.stktopsp = gp.sched.sp
gp.gcscanvalid = true
// malg returns status as _Gidle. Change to _Gdead before
// adding to allg where GC can see it. We use _Gdead to hide
// this from tracebacks and stack scans since it isn't a
Expand Down Expand Up @@ -2815,9 +2788,6 @@ func goexit0(gp *g) {
gp.gcAssistBytes = 0
}

// Note that gp's stack scan is now "valid" because it has no
// stack.
gp.gcscanvalid = true
dropg()

if GOARCH == "wasm" { // no threads yet on wasm
Expand Down Expand Up @@ -3462,7 +3432,6 @@ func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerp
if isSystemGoroutine(newg, false) {
atomic.Xadd(&sched.ngsys, +1)
}
newg.gcscanvalid = false
casgstatus(newg, _Gdead, _Grunnable)

if _p_.goidcache == _p_.goidcacheend {
Expand Down
1 change: 0 additions & 1 deletion src/runtime/runtime2.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,6 @@ type g struct {
preemptStop bool // transition to _Gpreempted on preemption; otherwise, just deschedule
paniconfault bool // panic (instead of crash) on unexpected fault address
gcscandone bool // g has scanned stack; protected by _Gscan bit in status
gcscanvalid bool // false at start of gc cycle, true if G has not run since last scan; TODO: remove?
throwsplit bool // must not split stack
raceignore int8 // ignore race detection events
sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/sizeof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestSizeof(t *testing.T) {
_32bit uintptr // size on 32bit platforms
_64bit uintptr // size on 64bit platforms
}{
{runtime.G{}, 216, 376}, // g, but exported for testing
{runtime.G{}, 212, 368}, // g, but exported for testing
}

for _, tt := range tests {
Expand Down

0 comments on commit 8c58615

Please sign in to comment.