diff options
author | Ian Lance Taylor <iant@google.com> | 2014-06-04 23:15:33 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2014-06-04 23:15:33 +0000 |
commit | bae90c989cb020d17a24919ec84c0b8dd2fae2da (patch) | |
tree | 89766166feb4ceca2d983169c5360e3f6f521b12 /libgo/go/sync | |
parent | 82b3da6a714493644a4333bfd8205e3341ed3b8e (diff) | |
download | gcc-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.go | 75 | ||||
-rw-r--r-- | libgo/go/sync/pool_test.go | 159 |
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() +} |