--- /dev/null
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sync
+
+// A Pool is a set of temporary objects that may be individually saved
+// and retrieved.
+//
+// Any item stored in the Pool may be removed automatically by the
+// implementation at any time without notification.
+// If the Pool holds the only reference when this happens, the item
+// might be deallocated.
+//
+// A Pool is safe for use by multiple goroutines simultaneously.
+//
+// This is an experimental package and might not be released.
+type Pool struct {
+ next *Pool // for use by runtime. must be first.
+ list []interface{} // offset known to runtime
+ mu Mutex // guards list
+
+ // New optionally specifies a function to generate
+ // a value when Get would otherwise return nil.
+ // It may not be changed concurrently with calls to Get.
+ New func() interface{}
+}
+
+func runtime_registerPool(*Pool)
+
+// Put adds x to the pool.
+func (p *Pool) Put(x interface{}) {
+ if x == nil {
+ return
+ }
+ p.mu.Lock()
+ if p.list == nil {
+ runtime_registerPool(p)
+ }
+ p.list = append(p.list, x)
+ p.mu.Unlock()
+}
+
+// Get selects an arbitrary item from the Pool, removes it from the
+// Pool, and returns it to the caller.
+// Get may choose to ignore the pool and treat it as empty.
+// Callers should not assume any relation between values passed to Put and
+// the values returned by Get.
+//
+// If Get would otherwise return nil and p.New is non-nil, Get returns
+// the result of calling p.New.
+func (p *Pool) Get() interface{} {
+ p.mu.Lock()
+ var x interface{}
+ if n := len(p.list); n > 0 {
+ x = p.list[n-1]
+ p.list[n-1] = nil // Just to be safe
+ p.list = p.list[:n-1]
+ }
+ p.mu.Unlock()
+ if x == nil && p.New != nil {
+ x = p.New()
+ }
+ return x
+}
--- /dev/null
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sync_test
+
+import (
+ "runtime"
+ "runtime/debug"
+ . "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+ "unsafe"
+)
+
+func TestPool(t *testing.T) {
+ // disable GC so we can control when it happens.
+ defer debug.SetGCPercent(debug.SetGCPercent(-1))
+ var p Pool
+ if p.Get() != nil {
+ t.Fatal("expected empty")
+ }
+ p.Put("a")
+ p.Put("b")
+ if g := p.Get(); g != "b" {
+ t.Fatalf("got %#v; want b", g)
+ }
+ if g := p.Get(); g != "a" {
+ t.Fatalf("got %#v; want a", g)
+ }
+ if g := p.Get(); g != nil {
+ t.Fatalf("got %#v; want nil", g)
+ }
+
+ p.Put("c")
+ debug.SetGCPercent(100) // to allow following GC to actually run
+ runtime.GC()
+ if g := p.Get(); g != nil {
+ t.Fatalf("got %#v; want nil after GC", g)
+ }
+}
+
+func TestPoolNew(t *testing.T) {
+ // disable GC so we can control when it happens.
+ defer debug.SetGCPercent(debug.SetGCPercent(-1))
+
+ i := 0
+ p := Pool{
+ New: func() interface{} {
+ i++
+ return i
+ },
+ }
+ if v := p.Get(); v != 1 {
+ t.Fatalf("got %v; want 1", v)
+ }
+ if v := p.Get(); v != 2 {
+ t.Fatalf("got %v; want 2", v)
+ }
+ p.Put(42)
+ if v := p.Get(); v != 42 {
+ t.Fatalf("got %v; want 42", v)
+ }
+ if v := p.Get(); v != 3 {
+ t.Fatalf("got %v; want 3", v)
+ }
+}
+
+// Test that Pool does not hold pointers to previously cached
+// resources
+func TestPoolGC(t *testing.T) {
+ var p Pool
+ var fin uint32
+ const N = 100
+ for i := 0; i < N; i++ {
+ v := new(int)
+ runtime.SetFinalizer(v, func(vv *int) {
+ 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.Millisecond)
+ // 1 pointer can remain on stack or elsewhere
+ if atomic.LoadUint32(&fin) >= N-1 {
+ return
+ }
+ }
+ t.Fatalf("only %v out of %v resources are finalized",
+ atomic.LoadUint32(&fin), N)
+}
+
+func TestPoolStress(t *testing.T) {
+ const P = 10
+ N := int(1e6)
+ if testing.Short() {
+ N /= 100
+ }
+ var p Pool
+ done := make(chan bool)
+ for i := 0; i < P; i++ {
+ go func() {
+ var v interface{} = 0
+ for j := 0; j < N; j++ {
+ if v == nil {
+ v = 0
+ }
+ p.Put(v)
+ v = p.Get()
+ if v != nil && v.(int) != 0 {
+ t.Fatalf("expect 0, got %v", v)
+ }
+ }
+ done <- true
+ }()
+ }
+ for i := 0; i < P; i++ {
+ <-done
+ }
+}
+
+func BenchmarkPool(b *testing.B) {
+ procs := runtime.GOMAXPROCS(-1)
+ var dec func() bool
+ if unsafe.Sizeof(b.N) == 8 {
+ n := int64(b.N)
+ dec = func() bool {
+ return atomic.AddInt64(&n, -1) >= 0
+ }
+ } else {
+ n := int32(b.N)
+ dec = func() bool {
+ return atomic.AddInt32(&n, -1) >= 0
+ }
+ }
+ var p Pool
+ var wg WaitGroup
+ for i := 0; i < procs; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for dec() {
+ p.Put(1)
+ p.Get()
+ }
+ }()
+ }
+ wg.Wait()
+}