1
0
mirror of https://github.com/golang/go synced 2024-09-25 05:20:13 -06:00

sync: add Pool type

Adds the Pool type and docs, and use it in fmt.
This is a temporary implementation, until Dmitry
makes it fast.

Uses the API proposal from Russ in http://goo.gl/cCKeb2 but
adds an optional New field, as used in fmt and elsewhere.
Almost all callers want that.

Update #4720

R=golang-dev, rsc, cshapiro, iant, r, dvyukov, khr
CC=golang-dev
https://golang.org/cl/41860043
This commit is contained in:
Brad Fitzpatrick 2013-12-18 11:08:34 -08:00
parent 6a23d82df1
commit 8c6ef061e3
3 changed files with 252 additions and 1 deletions

View File

@ -42,6 +42,36 @@ enum {
BitsEface = 3,
};
static struct
{
Lock;
void* head;
} pools;
void
sync·runtime_registerPool(void **p)
{
runtime·lock(&pools);
p[0] = pools.head;
pools.head = p;
runtime·unlock(&pools);
}
static void
clearpools(void)
{
void **p, **next;
for(p = pools.head; p != nil; p = next) {
next = p[0];
p[0] = nil; // next
p[1] = nil; // slice
p[2] = nil;
p[3] = nil;
}
pools.head = nil;
}
// Bits in per-word bitmap.
// #defines because enum might not be able to hold the values.
//
@ -2089,7 +2119,9 @@ runtime·gc(int32 force)
a.start_time = runtime·nanotime();
m->gcing = 1;
runtime·stoptheworld();
clearpools();
// Run gc on the g0 stack. We do this so that the g stack
// we're currently running on will no longer change. Cuts
// the root set down a bit (g0 stacks are not scanned, and

65
src/pkg/sync/pool.go Normal file
View File

@ -0,0 +1,65 @@
// 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
}

154
src/pkg/sync/pool_test.go Normal file
View File

@ -0,0 +1,154 @@
// 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()
}