aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2016-12-19 22:43:38 -0500
committerAustin Clements <austin@google.com>2016-12-20 17:27:47 +0000
commit0c942e8f2c6aeedfe672fdd0c2549ac39505cd75 (patch)
tree6c92f2c06d15b2917a5dcab89fb9c137d50f0a4e
parent860c9c0b8df6c0a2849fdd274a0a9f142cba3ea5 (diff)
downloadgo-0c942e8f2c6aeedfe672fdd0c2549ac39505cd75.tar.gz
go-0c942e8f2c6aeedfe672fdd0c2549ac39505cd75.zip
runtime: avoid incorrect panic when a signal arrives during STW
Stop-the-world and freeze-the-world (used for unhandled panics) are currently not safe to do at the same time. While a regular unhandled panic can't happen concurrently with STW (if the P hasn't been stopped, then the panic blocks the STW), a panic from a _SigThrow signal can happen on an already-stopped P, racing with STW. When this happens, freezetheworld sets sched.stopwait to 0x7fffffff and stopTheWorldWithSema panics because sched.stopwait != 0. Fix this by detecting when freeze-the-world happens before stop-the-world has completely stopped the world and freeze the STW operation rather than panicking. Fixes #17442. Change-Id: I646a7341221dd6d33ea21d818c2f7218e2cb7e20 Reviewed-on: https://go-review.googlesource.com/34611 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
-rw-r--r--src/runtime/proc.go32
1 files changed, 26 insertions, 6 deletions
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index cad1b1c0f4..756ce63c24 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -632,10 +632,15 @@ func helpgc(nproc int32) {
// sched.stopwait to in order to request that all Gs permanently stop.
const freezeStopWait = 0x7fffffff
+// freezing is set to non-zero if the runtime is trying to freeze the
+// world.
+var freezing uint32
+
// Similar to stopTheWorld but best-effort and can be called several times.
// There is no reverse operation, used during crashing.
// This function must not lock any mutexes.
func freezetheworld() {
+ atomic.Store(&freezing, 1)
// stopwait and preemption requests can be lost
// due to races with concurrently executing threads,
// so try several times
@@ -1018,15 +1023,30 @@ func stopTheWorldWithSema() {
preemptall()
}
}
+
+ // sanity checks
+ bad := ""
if sched.stopwait != 0 {
- throw("stopTheWorld: not stopped")
- }
- for i := 0; i < int(gomaxprocs); i++ {
- p := allp[i]
- if p.status != _Pgcstop {
- throw("stopTheWorld: not stopped")
+ bad = "stopTheWorld: not stopped (stopwait != 0)"
+ } else {
+ for i := 0; i < int(gomaxprocs); i++ {
+ p := allp[i]
+ if p.status != _Pgcstop {
+ bad = "stopTheWorld: not stopped (status != _Pgcstop)"
+ }
}
}
+ if atomic.Load(&freezing) != 0 {
+ // Some other thread is panicking. This can cause the
+ // sanity checks above to fail if the panic happens in
+ // the signal handler on a stopped thread. Either way,
+ // we should halt this thread.
+ lock(&deadlock)
+ lock(&deadlock)
+ }
+ if bad != "" {
+ throw(bad)
+ }
}
func mhelpgc() {