runtime: bound defer pools

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.

Change-Id: Iadcfb113ccecf912e1b64afc07926f0de9de2248
Reviewed-on: https://go-review.googlesource.com/3741
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Dmitry Vyukov 2015-02-03 08:58:04 +03:00
parent 71be013842
commit 8059071153
4 changed files with 66 additions and 18 deletions

View file

@ -166,9 +166,20 @@ func newdefer(siz int32) *_defer {
mp := acquirem()
if sc < uintptr(len(p{}.deferpool)) {
pp := mp.p
d = pp.deferpool[sc]
if d != nil {
pp.deferpool[sc] = d.link
if len(pp.deferpool[sc]) == 0 {
lock(&sched.deferlock)
for len(pp.deferpool[sc]) < cap(pp.deferpool[sc])/2 && sched.deferpool[sc] != nil {
d := sched.deferpool[sc]
sched.deferpool[sc] = d.link
d.link = nil
pp.deferpool[sc] = append(pp.deferpool[sc], d)
}
unlock(&sched.deferlock)
}
if ln := len(pp.deferpool[sc]); ln > 0 {
d = pp.deferpool[sc][ln-1]
pp.deferpool[sc][ln-1] = nil
pp.deferpool[sc] = pp.deferpool[sc][:ln-1]
}
}
if d == nil {
@ -214,9 +225,28 @@ func freedefer(d *_defer) {
if sc < uintptr(len(p{}.deferpool)) {
mp := acquirem()
pp := mp.p
if len(pp.deferpool[sc]) == cap(pp.deferpool[sc]) {
// Transfer half of local cache to the central cache.
var first, last *_defer
for len(pp.deferpool[sc]) > cap(pp.deferpool[sc])/2 {
ln := len(pp.deferpool[sc])
d := pp.deferpool[sc][ln-1]
pp.deferpool[sc][ln-1] = nil
pp.deferpool[sc] = pp.deferpool[sc][:ln-1]
if first == nil {
first = d
} else {
last.link = d
}
last = d
}
lock(&sched.deferlock)
last.link = sched.deferpool[sc]
sched.deferpool[sc] = first
unlock(&sched.deferlock)
}
*d = _defer{}
d.link = pp.deferpool[sc]
pp.deferpool[sc] = d
pp.deferpool[sc] = append(pp.deferpool[sc], d)
releasem(mp)
}
}