aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/preempt.go
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2019-10-25 16:17:41 -0400
committerAustin Clements <austin@google.com>2019-11-20 17:13:59 +0000
commitb89b4623eb70cbdc6b0aea43a5a826b7a26f20a7 (patch)
treedbe03bd384e2b2cf0ce13dd773a8c269c08612f7 /src/runtime/preempt.go
parent6fd467ee29226bf4b875921b7cb3b692c9db52ef (diff)
downloadgo-b89b4623eb70cbdc6b0aea43a5a826b7a26f20a7.tar.gz
go-b89b4623eb70cbdc6b0aea43a5a826b7a26f20a7.zip
runtime: support preemption on windows/{386,amd64}
This implements preemptM on Windows using SuspendThead and ResumeThread. Unlike on POSIX platforms, preemptM on Windows happens synchronously. This means we need a make a few other tweaks to suspendG: 1. We need to CAS the G back to _Grunning before doing the preemptM, or there's a good chance we'll just catch the G spinning on its status in the runtime, which won't be preemptible. 2. We need to rate-limit preemptM attempts. Otherwise, if the first attempt catches the G at a non-preemptible point, the busy loop in suspendG may hammer it so hard that it never makes it past that non-preemptible point. Updates #10958, #24543. Change-Id: Ie53b098811096f7e45d864afd292dc9e999ce226 Reviewed-on: https://go-review.googlesource.com/c/go/+/204340 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
Diffstat (limited to 'src/runtime/preempt.go')
-rw-r--r--src/runtime/preempt.go31
1 files changed, 25 insertions, 6 deletions
diff --git a/src/runtime/preempt.go b/src/runtime/preempt.go
index f154614913..60e1bcef5f 100644
--- a/src/runtime/preempt.go
+++ b/src/runtime/preempt.go
@@ -118,6 +118,7 @@ func suspendG(gp *g) suspendGState {
stopped := false
var asyncM *m
var asyncGen uint32
+ var nextPreemptM int64
for i := 0; ; i++ {
switch s := readgstatus(gp); s {
default:
@@ -205,14 +206,32 @@ func suspendG(gp *g) suspendGState {
gp.preempt = true
gp.stackguard0 = stackPreempt
- // Send asynchronous preemption.
- asyncM = gp.m
- asyncGen = atomic.Load(&asyncM.preemptGen)
- if preemptMSupported && debug.asyncpreemptoff == 0 {
- preemptM(asyncM)
- }
+ // Prepare for asynchronous preemption.
+ asyncM2 := gp.m
+ asyncGen2 := atomic.Load(&asyncM2.preemptGen)
+ needAsync := asyncM != asyncM2 || asyncGen != asyncGen2
+ asyncM = asyncM2
+ asyncGen = asyncGen2
casfrom_Gscanstatus(gp, _Gscanrunning, _Grunning)
+
+ // Send asynchronous preemption. We do this
+ // after CASing the G back to _Grunning
+ // because preemptM may be synchronous and we
+ // don't want to catch the G just spinning on
+ // its status.
+ if preemptMSupported && debug.asyncpreemptoff == 0 && needAsync {
+ // Rate limit preemptM calls. This is
+ // particularly important on Windows
+ // where preemptM is actually
+ // synchronous and the spin loop here
+ // can lead to live-lock.
+ now := nanotime()
+ if now >= nextPreemptM {
+ nextPreemptM = now + yieldDelay/2
+ preemptM(asyncM)
+ }
+ }
}
// TODO: Don't busy wait. This loop should really only