Commit af3868f1 authored by Dmitriy Vyukov's avatar Dmitriy Vyukov

sync: release Pool memory during second and later GCs

Pool memory was only being released during the first GC after the first Put.

Put assumes that p.local != nil means p is on the allPools list.
poolCleanup (called during each GC) removed each pool from allPools
but did not clear p.local, so each pool was cleared by exactly one GC
and then never cleared again.

This bug was introduced late in the Go 1.3 release cycle.

Fixes #8979.

LGTM=rsc
R=golang-codereviews, bradfitz, r, rsc
CC=golang-codereviews, khr
https://golang.org/cl/162980043
parent 18051c08
...@@ -200,6 +200,8 @@ func poolCleanup() { ...@@ -200,6 +200,8 @@ func poolCleanup() {
} }
l.shared = nil l.shared = nil
} }
p.local = nil
p.localSize = 0
} }
allPools = []*Pool{} allPools = []*Pool{}
} }
......
...@@ -69,32 +69,44 @@ func TestPoolNew(t *testing.T) { ...@@ -69,32 +69,44 @@ func TestPoolNew(t *testing.T) {
} }
} }
// Test that Pool does not hold pointers to previously cached // Test that Pool does not hold pointers to previously cached resources.
// resources
func TestPoolGC(t *testing.T) { func TestPoolGC(t *testing.T) {
testPool(t, true)
}
// Test that Pool releases resources on GC.
func TestPoolRelease(t *testing.T) {
testPool(t, false)
}
func testPool(t *testing.T, drain bool) {
var p Pool var p Pool
var fin uint32
const N = 100 const N = 100
for i := 0; i < N; i++ { loop:
v := new(string) for try := 0; try < 3; try++ {
runtime.SetFinalizer(v, func(vv *string) { var fin, fin1 uint32
atomic.AddUint32(&fin, 1) for i := 0; i < N; i++ {
}) v := new(string)
p.Put(v) runtime.SetFinalizer(v, func(vv *string) {
} atomic.AddUint32(&fin, 1)
for i := 0; i < N; i++ { })
p.Get() p.Put(v)
} }
for i := 0; i < 5; i++ { if drain {
runtime.GC() for i := 0; i < N; i++ {
time.Sleep(time.Duration(i*100+10) * time.Millisecond) p.Get()
// 1 pointer can remain on stack or elsewhere }
if atomic.LoadUint32(&fin) >= N-1 { }
return for i := 0; i < 5; i++ {
runtime.GC()
time.Sleep(time.Duration(i*100+10) * time.Millisecond)
// 1 pointer can remain on stack or elsewhere
if fin1 = atomic.LoadUint32(&fin); fin1 >= N-1 {
continue loop
}
} }
t.Fatalf("only %v out of %v resources are finalized on try %v", fin1, N, try)
} }
t.Fatalf("only %v out of %v resources are finalized",
atomic.LoadUint32(&fin), N)
} }
func TestPoolStress(t *testing.T) { func TestPoolStress(t *testing.T) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment