aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2020-10-30 15:22:52 -0400
committerMichael Pratt <mpratt@google.com>2020-10-30 21:02:17 +0000
commit420c68dd68c648af6642dd7e5cf6dacf9f067f6e (patch)
tree4884a9bca4d4ab0af21544ffbd349fe079293169 /src/runtime
parent84d7a85089009332756c18e876ec91f96b362ebf (diff)
downloadgo-420c68dd68c648af6642dd7e5cf6dacf9f067f6e.tar.xz
runtime: tighten systemstack in lock assertions
We use systemstack on the locking path to avoid stack splits which could cause locks to be recorded out of order (see comment on lockWithRank). This concern is irrelevant on lock assertions, where we simply need to see if a lock is held and don't care if another is taken in the meantime. Thus we can simply drop these unless we actually need to crash. Updates #40677 Change-Id: I85d730913a59867753ee1ed0386f8c5efda5c432 Reviewed-on: https://go-review.googlesource.com/c/go/+/266718 Run-TryBot: Michael Pratt <mpratt@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com> Trust: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/lockrank_on.go76
1 files changed, 47 insertions, 29 deletions
diff --git a/src/runtime/lockrank_on.go b/src/runtime/lockrank_on.go
index c25b3a4656..88ac95a004 100644
--- a/src/runtime/lockrank_on.go
+++ b/src/runtime/lockrank_on.go
@@ -95,7 +95,8 @@ func lockWithRank(l *mutex, rank lockRank) {
})
}
-//go:systemstack
+// nosplit to ensure it can be called in as many contexts as possible.
+//go:nosplit
func printHeldLocks(gp *g) {
if gp.m.locksHeldLen == 0 {
println("<none>")
@@ -113,7 +114,7 @@ func printHeldLocks(gp *g) {
//go:nosplit
func acquireLockRank(rank lockRank) {
gp := getg()
- // Log the new class.
+ // Log the new class. See comment on lockWithRank.
systemstack(func() {
i := gp.m.locksHeldLen
if i >= len(gp.m.locksHeld) {
@@ -238,7 +239,8 @@ func lockWithRankMayAcquire(l *mutex, rank lockRank) {
})
}
-//go:systemstack
+// nosplit to ensure it can be called in as many contexts as possible.
+//go:nosplit
func checkLockHeld(gp *g, l *mutex) bool {
for i := gp.m.locksHeldLen - 1; i >= 0; i-- {
if gp.m.locksHeld[i].lockAddr == uintptr(unsafe.Pointer(l)) {
@@ -255,14 +257,18 @@ func checkLockHeld(gp *g, l *mutex) bool {
func assertLockHeld(l *mutex) {
gp := getg()
+ held := checkLockHeld(gp, l)
+ if held {
+ return
+ }
+
+ // Crash from system stack to avoid splits that may cause
+ // additional issues.
systemstack(func() {
- held := checkLockHeld(gp, l)
- if !held {
- printlock()
- print("caller requires lock ", l, " (rank ", l.rank.String(), "), holding:\n")
- printHeldLocks(gp)
- throw("not holding required lock!")
- }
+ printlock()
+ print("caller requires lock ", l, " (rank ", l.rank.String(), "), holding:\n")
+ printHeldLocks(gp)
+ throw("not holding required lock!")
})
}
@@ -276,13 +282,15 @@ func assertLockHeld(l *mutex) {
func assertRankHeld(r lockRank) {
gp := getg()
- systemstack(func() {
- for i := gp.m.locksHeldLen - 1; i >= 0; i-- {
- if gp.m.locksHeld[i].rank == r {
- return
- }
+ for i := gp.m.locksHeldLen - 1; i >= 0; i-- {
+ if gp.m.locksHeld[i].rank == r {
+ return
}
+ }
+ // Crash from system stack to avoid splits that may cause
+ // additional issues.
+ systemstack(func() {
printlock()
print("caller requires lock with rank ", r.String(), "), holding:\n")
printHeldLocks(gp)
@@ -298,8 +306,10 @@ func assertRankHeld(r lockRank) {
//go:nosplit
func worldStopped() {
if stopped := atomic.Xadd(&worldIsStopped, 1); stopped != 1 {
- print("world stop count=", stopped, "\n")
- throw("recursive world stop")
+ systemstack(func() {
+ print("world stop count=", stopped, "\n")
+ throw("recursive world stop")
+ })
}
}
@@ -311,8 +321,10 @@ func worldStopped() {
//go:nosplit
func worldStarted() {
if stopped := atomic.Xadd(&worldIsStopped, -1); stopped != 0 {
- print("world stop count=", stopped, "\n")
- throw("released non-stopped world stop")
+ systemstack(func() {
+ print("world stop count=", stopped, "\n")
+ throw("released non-stopped world stop")
+ })
}
}
@@ -321,8 +333,10 @@ func worldStarted() {
func checkWorldStopped() bool {
stopped := atomic.Load(&worldIsStopped)
if stopped > 1 {
- print("inconsistent world stop count=", stopped, "\n")
- throw("inconsistent world stop count")
+ systemstack(func() {
+ print("inconsistent world stop count=", stopped, "\n")
+ throw("inconsistent world stop count")
+ })
}
return stopped == 1
@@ -352,14 +366,18 @@ func assertWorldStoppedOrLockHeld(l *mutex) {
}
gp := getg()
+ held := checkLockHeld(gp, l)
+ if held {
+ return
+ }
+
+ // Crash from system stack to avoid splits that may cause
+ // additional issues.
systemstack(func() {
- held := checkLockHeld(gp, l)
- if !held {
- printlock()
- print("caller requires world stop or lock ", l, " (rank ", l.rank.String(), "), holding:\n")
- println("<no world stop>")
- printHeldLocks(gp)
- throw("no world stop or required lock!")
- }
+ printlock()
+ print("caller requires world stop or lock ", l, " (rank ", l.rank.String(), "), holding:\n")
+ println("<no world stop>")
+ printHeldLocks(gp)
+ throw("no world stop or required lock!")
})
}