diff options
author | Austin Clements <austin@google.com> | 2016-12-19 22:43:38 -0500 |
---|---|---|
committer | Austin Clements <austin@google.com> | 2016-12-20 17:27:47 +0000 |
commit | 0c942e8f2c6aeedfe672fdd0c2549ac39505cd75 (patch) | |
tree | 6c92f2c06d15b2917a5dcab89fb9c137d50f0a4e | |
parent | 860c9c0b8df6c0a2849fdd274a0a9f142cba3ea5 (diff) | |
download | go-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.go | 32 |
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() { |