mirror of
https://github.com/golang/go
synced 2024-11-12 08:20:22 -07: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:
parent
6a23d82df1
commit
8c6ef061e3
@ -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.
|
||||
//
|
||||
@ -2090,6 +2120,8 @@ runtime·gc(int32 force)
|
||||
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
65
src/pkg/sync/pool.go
Normal 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
154
src/pkg/sync/pool_test.go
Normal 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()
|
||||
}
|
Loading…
Reference in New Issue
Block a user