From: Austin Clements Date: Tue, 20 Dec 2016 03:43:38 +0000 (-0500) Subject: runtime: avoid incorrect panic when a signal arrives during STW X-Git-Tag: go1.8rc1~75 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0c942e8f2c6aeedfe672fdd0c2549ac39505cd75;p=gostls13.git 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 TryBot-Result: Gobot Gobot Reviewed-by: Russ Cox Reviewed-by: Ian Lance Taylor --- 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() {