aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/sync
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2014-06-04 23:15:33 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2014-06-04 23:15:33 +0000
commitbae90c989cb020d17a24919ec84c0b8dd2fae2da (patch)
tree89766166feb4ceca2d983169c5360e3f6f521b12 /libgo/go/sync
parent82b3da6a714493644a4333bfd8205e3341ed3b8e (diff)
downloadgcc-bae90c989cb020d17a24919ec84c0b8dd2fae2da.zip
gcc-bae90c989cb020d17a24919ec84c0b8dd2fae2da.tar.gz
gcc-bae90c989cb020d17a24919ec84c0b8dd2fae2da.tar.bz2
libgo: Merge from revision 18783:00cce3a34d7e of master library.
This revision was committed January 7, 2014. The next revision deleted runtime/mfinal.c. That will be done in a subsequent merge. This merge changes type descriptors to add a zero field, pointing to a zero value for that type. This is implemented as a common variable. * go-gcc.cc (Gcc_backend::implicit_variable): Add is_common and alignment parameters. Permit init parameter to be NULL. From-SVN: r211249
Diffstat (limited to 'libgo/go/sync')
-rw-r--r--libgo/go/sync/pool.go75
-rw-r--r--libgo/go/sync/pool_test.go159
2 files changed, 234 insertions, 0 deletions
diff --git a/libgo/go/sync/pool.go b/libgo/go/sync/pool.go
new file mode 100644
index 0000000..9eb07c3
--- /dev/null
+++ b/libgo/go/sync/pool.go
@@ -0,0 +1,75 @@
+// 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.
+//
+// Pool's intended use is for free lists maintained in global variables,
+// typically accessed by multiple goroutines simultaneously. Using a
+// Pool instead of a custom free list allows the runtime to reclaim
+// entries from the pool when it makes sense to do so. An
+// appropriate use of sync.Pool is to create a pool of temporary buffers
+// shared between independent clients of a global resource. On the
+// other hand, if a free list is maintained as part of an object used
+// only by a single client and freed when the client completes,
+// implementing that free list as a Pool is not appropriate.
+//
+// This is an experimental type 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
+}
diff --git a/libgo/go/sync/pool_test.go b/libgo/go/sync/pool_test.go
new file mode 100644
index 0000000..f88dab4
--- /dev/null
+++ b/libgo/go/sync/pool_test.go
@@ -0,0 +1,159 @@
+// 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
+ }
+
+ // gccgo has a less precise heap.
+ if runtime.Compiler == "gccgo" && atomic.LoadUint32(&fin) >= N-5 {
+ 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()
+}