runtime: bound defer pools (try 2)

The unbounded list-based defer pool can grow infinitely.
This can happen if a goroutine routinely allocates a defer;
then blocks on one P; and then unblocked, scheduled and
frees the defer on another P.
The scenario was reported on golang-nuts list.

We've been here several times. Any unbounded local caches
are bad and grow to infinite size. This change introduces
central defer pool; local pools become fixed-size
with the only purpose of amortizing accesses to the
central pool.

Freedefer now executes on system stack to not consume
nosplit stack space.

Change-Id: I1a27695838409259d1586a0adfa9f92bccf7ceba
Reviewed-on: https://go-review.googlesource.com/3967
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Dmitry Vyukov <dvyukov@google.com>
This commit is contained in:
Dmitry Vyukov 2015-02-05 13:35:41 +00:00
parent 5ef145c809
commit b759e225f5
4 changed files with 70 additions and 20 deletions

View file

@ -314,7 +314,9 @@ type p struct {
syscalltick uint32 // incremented on every system call
m *m // back-link to associated m (nil if idle)
mcache *mcache
deferpool [5]*_defer // pool of available defer structs of different sizes (see panic.c)
deferpool [5][]*_defer // pool of available defer structs of different sizes (see panic.c)
deferpoolbuf [5][32]*_defer
// Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen.
goidcache uint64
@ -372,6 +374,10 @@ type schedt struct {
sudoglock mutex
sudogcache *sudog
// Central pool of available defer structs of different sizes.
deferlock mutex
deferpool [5]*_defer
gcwaiting uint32 // gc is waiting to run
stopwait int32
stopnote note