]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: avoid incorrect panic when a signal arrives during STW
authorAustin Clements <austin@google.com>
Tue, 20 Dec 2016 03:43:38 +0000 (22:43 -0500)
committerAustin Clements <austin@google.com>
Tue, 20 Dec 2016 17:27:47 +0000 (17:27 +0000)
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>
src/runtime/proc.go

index cad1b1c0f45319f9b6b5d4bef79be60328d18d7a..756ce63c2421f13fcf19e0b1e5fdf50400fb15d7 100644 (file)
@@ -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() {