mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: fix race in scanvalid assertion
Change-Id: I389b2e10fe667eaa55f87b71b1e004994694d4a3 Reviewed-on: https://go-review.googlesource.com/11173 Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
f0fee976aa
commit
cfa3eda587
1 changed files with 21 additions and 4 deletions
|
|
@ -248,6 +248,18 @@ func readgstatus(gp *g) uint32 {
|
||||||
return atomicload(&gp.atomicstatus)
|
return atomicload(&gp.atomicstatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ownership of gscanvalid:
|
||||||
|
//
|
||||||
|
// If gp is running (meaning status == _Grunning or _Grunning|_Gscan),
|
||||||
|
// then gp owns gp.gscanvalid, 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 gscanvalid, 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 gscanvalid.
|
||||||
|
|
||||||
// The Gscanstatuses are acting like locks and this releases them.
|
// 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
|
// 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
|
// simple atomic stores but for now we are going to throw if
|
||||||
|
|
@ -294,10 +306,6 @@ func castogscanstatus(gp *g, oldval, newval uint32) bool {
|
||||||
return cas(&gp.atomicstatus, oldval, newval)
|
return cas(&gp.atomicstatus, oldval, newval)
|
||||||
}
|
}
|
||||||
case _Grunning:
|
case _Grunning:
|
||||||
if gp.gcscanvalid {
|
|
||||||
print("runtime: castogscanstatus _Grunning and gp.gcscanvalid is true, newval=", hex(newval), "\n")
|
|
||||||
throw("castogscanstatus")
|
|
||||||
}
|
|
||||||
if newval == _Gscanrunning || newval == _Gscanenqueue {
|
if newval == _Gscanrunning || newval == _Gscanenqueue {
|
||||||
return cas(&gp.atomicstatus, oldval, newval)
|
return cas(&gp.atomicstatus, oldval, newval)
|
||||||
}
|
}
|
||||||
|
|
@ -320,6 +328,15 @@ 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.
|
||||||
|
print("runtime: casgstatus ", hex(oldval), "->", hex(newval), " gp.status=", hex(gp.atomicstatus), " gp.gcscanvalid=true\n")
|
||||||
|
throw("casgstatus")
|
||||||
|
}
|
||||||
|
|
||||||
// loop if gp->atomicstatus is in a scan state giving
|
// loop if gp->atomicstatus is in a scan state giving
|
||||||
// GC time to finish and change the state to oldval.
|
// GC time to finish and change the state to oldval.
|
||||||
for !cas(&gp.atomicstatus, oldval, newval) {
|
for !cas(&gp.atomicstatus, oldval, newval) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue