]> Cypherpunks repositories - gostls13.git/commitdiff
sync: release Pool memory during second and later GCs
authorDmitriy Vyukov <dvyukov@google.com>
Wed, 22 Oct 2014 16:23:49 +0000 (20:23 +0400)
committerDmitriy Vyukov <dvyukov@google.com>
Wed, 22 Oct 2014 16:23:49 +0000 (20:23 +0400)
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

src/sync/pool.go
src/sync/pool_test.go

index 1f08707cd42269dbc654d4a2cf9e8f88753984a4..0cf0637024402a9fb0f5a9bf99b28463286f5dd9 100644 (file)
@@ -200,6 +200,8 @@ func poolCleanup() {
                        }
                        l.shared = nil
                }
+               p.local = nil
+               p.localSize = 0
        }
        allPools = []*Pool{}
 }
index cf5c8bd903f8d37d469bd1309dd57d7c40d64c7a..fa1a27beac998254f235baa5c22d81588ba97b54 100644 (file)
@@ -69,32 +69,44 @@ func TestPoolNew(t *testing.T) {
        }
 }
 
-// Test that Pool does not hold pointers to previously cached
-// resources
+// Test that Pool does not hold pointers to previously cached resources.
 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 fin uint32
        const N = 100
-       for i := 0; i < N; i++ {
-               v := new(string)
-               runtime.SetFinalizer(v, func(vv *string) {
-                       atomic.AddUint32(&fin, 1)
-               })
-               p.Put(v)
-       }
-       for i := 0; i < N; i++ {
-               p.Get()
-       }
-       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 atomic.LoadUint32(&fin) >= N-1 {
-                       return
+loop:
+       for try := 0; try < 3; try++ {
+               var fin, fin1 uint32
+               for i := 0; i < N; i++ {
+                       v := new(string)
+                       runtime.SetFinalizer(v, func(vv *string) {
+                               atomic.AddUint32(&fin, 1)
+                       })
+                       p.Put(v)
+               }
+               if drain {
+                       for i := 0; i < N; i++ {
+                               p.Get()
+                       }
+               }
+               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) {