runtime: don't steal work when a STW is pending
A spinning M's role is to find runnable goroutines for its P, stealing
from other P's runnext slot as a last resort. When it comes to that, the
spinning M does a brief sleep to double-check that the victim P isn't
about to run the runnext goroutine itself. The target of the sleep is
3us, but the OS's timer slack can stretch it to beyond 50us.
During that sleep, another thread may have announced a stop-the-world.
When the spinning M wakes up, it easily steals the runnext goroutine
(the victim P has observed the STW request and has gone to sleep),
returns it to findRunnable, and soon the (formerly) spinning M begins
executing a goroutine even though the rest of the world is trying to
come to a stop.
After that wakeup, double-check that there's no pending STW before
committing to the runnext steal.
For #81657
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index b49f132..bb18ed2 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -7765,6 +7765,11 @@
// optimization. So just yield.
osyield()
}
+ if sched.gcwaiting.Load() {
+ // The sleep above might have overlapped with a STW
+ // request. Check before committing to this new work.
+ return 0
+ }
}
}
}
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +2 |
if sched.gcwaiting.Load() {
// GC work may be available.
return nil, false, now, pollUntil, true
}maybe we want to move this after runqsteal. it's a bit weird that we can return from it early because of gcwaiting, but still end up returning with newWork=false.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
if sched.gcwaiting.Load() {
// GC work may be available.
return nil, false, now, pollUntil, true
}maybe we want to move this after runqsteal. it's a bit weird that we can return from it early because of gcwaiting, but still end up returning with newWork=false.
Checking after sounds good, I've moved it down.
(That delays the first check in stealWork, and grows one of the time windows where the scheduler goes without checking sched.gcwaiting. If it turns out that's a problem, we can come up with more general guidance on how frequently the scheduler should re-check that condition.)
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Auto-Submit | +1 |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
runtime: don't steal work when a STW is pending
A spinning M's role is to find runnable goroutines for its P, stealing
from other P's runnext slot as a last resort. When it comes to that, the
spinning M does a brief sleep to double-check that the victim P isn't
about to run the runnext goroutine itself. The target of the sleep is
3us, but the OS's timer slack can stretch it to beyond 50us.
During that sleep, another thread may have announced a stop-the-world.
When the spinning M wakes up, it easily steals the runnext goroutine
(the victim P has observed the STW request and has gone to sleep),
returns it to findRunnable, and soon the (formerly) spinning M begins
executing a goroutine even though the rest of the world is trying to
come to a stop.
After that wakeup, double-check that there's no pending STW before
committing to the runnext steal.
For #81657
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |