Home | History | Annotate | Download | only in sync
      1 // Copyright 2013 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Pool is no-op under race detector, so all these tests do not work.
      6 // +build !race
      7 
      8 package sync_test
      9 
     10 import (
     11 	"runtime"
     12 	"runtime/debug"
     13 	. "sync"
     14 	"sync/atomic"
     15 	"testing"
     16 	"time"
     17 )
     18 
     19 func TestPool(t *testing.T) {
     20 	// disable GC so we can control when it happens.
     21 	defer debug.SetGCPercent(debug.SetGCPercent(-1))
     22 	var p Pool
     23 	if p.Get() != nil {
     24 		t.Fatal("expected empty")
     25 	}
     26 	p.Put("a")
     27 	p.Put("b")
     28 	if g := p.Get(); g != "a" {
     29 		t.Fatalf("got %#v; want a", g)
     30 	}
     31 	if g := p.Get(); g != "b" {
     32 		t.Fatalf("got %#v; want b", g)
     33 	}
     34 	if g := p.Get(); g != nil {
     35 		t.Fatalf("got %#v; want nil", g)
     36 	}
     37 
     38 	p.Put("c")
     39 	debug.SetGCPercent(100) // to allow following GC to actually run
     40 	runtime.GC()
     41 	if g := p.Get(); g != nil {
     42 		t.Fatalf("got %#v; want nil after GC", g)
     43 	}
     44 }
     45 
     46 func TestPoolNew(t *testing.T) {
     47 	// disable GC so we can control when it happens.
     48 	defer debug.SetGCPercent(debug.SetGCPercent(-1))
     49 
     50 	i := 0
     51 	p := Pool{
     52 		New: func() interface{} {
     53 			i++
     54 			return i
     55 		},
     56 	}
     57 	if v := p.Get(); v != 1 {
     58 		t.Fatalf("got %v; want 1", v)
     59 	}
     60 	if v := p.Get(); v != 2 {
     61 		t.Fatalf("got %v; want 2", v)
     62 	}
     63 	p.Put(42)
     64 	if v := p.Get(); v != 42 {
     65 		t.Fatalf("got %v; want 42", v)
     66 	}
     67 	if v := p.Get(); v != 3 {
     68 		t.Fatalf("got %v; want 3", v)
     69 	}
     70 }
     71 
     72 // Test that Pool does not hold pointers to previously cached resources.
     73 func TestPoolGC(t *testing.T) {
     74 	testPool(t, true)
     75 }
     76 
     77 // Test that Pool releases resources on GC.
     78 func TestPoolRelease(t *testing.T) {
     79 	testPool(t, false)
     80 }
     81 
     82 func testPool(t *testing.T, drain bool) {
     83 	var p Pool
     84 	const N = 100
     85 loop:
     86 	for try := 0; try < 3; try++ {
     87 		var fin, fin1 uint32
     88 		for i := 0; i < N; i++ {
     89 			v := new(string)
     90 			runtime.SetFinalizer(v, func(vv *string) {
     91 				atomic.AddUint32(&fin, 1)
     92 			})
     93 			p.Put(v)
     94 		}
     95 		if drain {
     96 			for i := 0; i < N; i++ {
     97 				p.Get()
     98 			}
     99 		}
    100 		for i := 0; i < 5; i++ {
    101 			runtime.GC()
    102 			time.Sleep(time.Duration(i*100+10) * time.Millisecond)
    103 			// 1 pointer can remain on stack or elsewhere
    104 			if fin1 = atomic.LoadUint32(&fin); fin1 >= N-1 {
    105 				continue loop
    106 			}
    107 		}
    108 		t.Fatalf("only %v out of %v resources are finalized on try %v", fin1, N, try)
    109 	}
    110 }
    111 
    112 func TestPoolStress(t *testing.T) {
    113 	const P = 10
    114 	N := int(1e6)
    115 	if testing.Short() {
    116 		N /= 100
    117 	}
    118 	var p Pool
    119 	done := make(chan bool)
    120 	for i := 0; i < P; i++ {
    121 		go func() {
    122 			var v interface{} = 0
    123 			for j := 0; j < N; j++ {
    124 				if v == nil {
    125 					v = 0
    126 				}
    127 				p.Put(v)
    128 				v = p.Get()
    129 				if v != nil && v.(int) != 0 {
    130 					t.Errorf("expect 0, got %v", v)
    131 					break
    132 				}
    133 			}
    134 			done <- true
    135 		}()
    136 	}
    137 	for i := 0; i < P; i++ {
    138 		<-done
    139 	}
    140 }
    141 
    142 func BenchmarkPool(b *testing.B) {
    143 	var p Pool
    144 	b.RunParallel(func(pb *testing.PB) {
    145 		for pb.Next() {
    146 			p.Put(1)
    147 			p.Get()
    148 		}
    149 	})
    150 }
    151 
    152 func BenchmarkPoolOverflow(b *testing.B) {
    153 	var p Pool
    154 	b.RunParallel(func(pb *testing.PB) {
    155 		for pb.Next() {
    156 			for b := 0; b < 100; b++ {
    157 				p.Put(1)
    158 			}
    159 			for b := 0; b < 100; b++ {
    160 				p.Get()
    161 			}
    162 		}
    163 	})
    164 }
    165