1
0
mirror of https://github.com/golang/go synced 2024-11-16 19:24:49 -07:00

runtime: release worldsema with a direct G handoff

Currently worldsema is not released with direct handoff, so the
semaphore is an unfair synchronization mechanism. If, for example,
ReadMemStats is called in a loop, it can continuously stomp on attempts
by the GC to stop the world.

Note that it's specifically possible for ReadMemStats to delay a STW to
end GC since ReadMemStats is able to STW during a GC since #19112 was
fixed.

While this particular case is unlikely and the right answer in most
applications is to simply not call such an expensive operation in a
loop, this pattern is used often in tests.

Fixes #40459.

Change-Id: Ia4a54f0fd956ea145a319f9f06c4cd37dd52fd8a
Reviewed-on: https://go-review.googlesource.com/c/go/+/243977
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Michael Anthony Knyszek 2020-07-20 18:19:56 +00:00 committed by Michael Knyszek
parent f96b62be2e
commit 64a9a75ce9

View File

@ -961,10 +961,26 @@ func stopTheWorld(reason string) {
// startTheWorld undoes the effects of stopTheWorld.
func startTheWorld() {
systemstack(func() { startTheWorldWithSema(false) })
// worldsema must be held over startTheWorldWithSema to ensure
// gomaxprocs cannot change while worldsema is held.
semrelease(&worldsema)
getg().m.preemptoff = ""
//
// Release worldsema with direct handoff to the next waiter, but
// acquirem so that semrelease1 doesn't try to yield our time.
//
// Otherwise if e.g. ReadMemStats is being called in a loop,
// it might stomp on other attempts to stop the world, such as
// for starting or ending GC. The operation this blocks is
// so heavy-weight that we should just try to be as fair as
// possible here.
//
// We don't want to just allow us to get preempted between now
// and releasing the semaphore because then we keep everyone
// (including, for example, GCs) waiting longer.
mp := acquirem()
mp.preemptoff = ""
semrelease1(&worldsema, true, 0)
releasem(mp)
}
// stopTheWorldGC has the same effect as stopTheWorld, but blocks