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 
     27 	// Make sure that the goroutine doesn't migrate to another P
     28 	// between Put and Get calls.
     29 	Runtime_procPin()
     30 	p.Put("a")
     31 	p.Put("b")
     32 	if g := p.Get(); g != "a" {
     33 		t.Fatalf("got %#v; want a", g)
     34 	}
     35 	if g := p.Get(); g != "b" {
     36 		t.Fatalf("got %#v; want b", g)
     37 	}
     38 	if g := p.Get(); g != nil {
     39 		t.Fatalf("got %#v; want nil", g)
     40 	}
     41 	Runtime_procUnpin()
     42 
     43 	p.Put("c")
     44 	debug.SetGCPercent(100) // to allow following GC to actually run
     45 	runtime.GC()
     46 	if g := p.Get(); g != nil {
     47 		t.Fatalf("got %#v; want nil after GC", g)
     48 	}
     49 }
     50 
     51 func TestPoolNew(t *testing.T) {
     52 	// disable GC so we can control when it happens.
     53 	defer debug.SetGCPercent(debug.SetGCPercent(-1))
     54 
     55 	i := 0
     56 	p := Pool{
     57 		New: func() interface{} {
     58 			i++
     59 			return i
     60 		},
     61 	}
     62 	if v := p.Get(); v != 1 {
     63 		t.Fatalf("got %v; want 1", v)
     64 	}
     65 	if v := p.Get(); v != 2 {
     66 		t.Fatalf("got %v; want 2", v)
     67 	}
     68 
     69 	// Make sure that the goroutine doesn't migrate to another P
     70 	// between Put and Get calls.
     71 	Runtime_procPin()
     72 	p.Put(42)
     73 	if v := p.Get(); v != 42 {
     74 		t.Fatalf("got %v; want 42", v)
     75 	}
     76 	Runtime_procUnpin()
     77 
     78 	if v := p.Get(); v != 3 {
     79 		t.Fatalf("got %v; want 3", v)
     80 	}
     81 }
     82 
     83 // Test that Pool does not hold pointers to previously cached resources.
     84 func TestPoolGC(t *testing.T) {
     85 	testPool(t, true)
     86 }
     87 
     88 // Test that Pool releases resources on GC.
     89 func TestPoolRelease(t *testing.T) {
     90 	testPool(t, false)
     91 }
     92 
     93 func testPool(t *testing.T, drain bool) {
     94 	var p Pool
     95 	const N = 100
     96 loop:
     97 	for try := 0; try < 3; try++ {
     98 		var fin, fin1 uint32
     99 		for i := 0; i < N; i++ {
    100 			v := new(string)
    101 			runtime.SetFinalizer(v, func(vv *string) {
    102 				atomic.AddUint32(&fin, 1)
    103 			})
    104 			p.Put(v)
    105 		}
    106 		if drain {
    107 			for i := 0; i < N; i++ {
    108 				p.Get()
    109 			}
    110 		}
    111 		for i := 0; i < 5; i++ {
    112 			runtime.GC()
    113 			time.Sleep(time.Duration(i*100+10) * time.Millisecond)
    114 			// 1 pointer can remain on stack or elsewhere
    115 			if fin1 = atomic.LoadUint32(&fin); fin1 >= N-1 {
    116 				continue loop
    117 			}
    118 		}
    119 		t.Fatalf("only %v out of %v resources are finalized on try %v", fin1, N, try)
    120 	}
    121 }
    122 
    123 func TestPoolStress(t *testing.T) {
    124 	const P = 10
    125 	N := int(1e6)
    126 	if testing.Short() {
    127 		N /= 100
    128 	}
    129 	var p Pool
    130 	done := make(chan bool)
    131 	for i := 0; i < P; i++ {
    132 		go func() {
    133 			var v interface{} = 0
    134 			for j := 0; j < N; j++ {
    135 				if v == nil {
    136 					v = 0
    137 				}
    138 				p.Put(v)
    139 				v = p.Get()
    140 				if v != nil && v.(int) != 0 {
    141 					t.Errorf("expect 0, got %v", v)
    142 					break
    143 				}
    144 			}
    145 			done <- true
    146 		}()
    147 	}
    148 	for i := 0; i < P; i++ {
    149 		<-done
    150 	}
    151 }
    152 
    153 func BenchmarkPool(b *testing.B) {
    154 	var p Pool
    155 	b.RunParallel(func(pb *testing.PB) {
    156 		for pb.Next() {
    157 			p.Put(1)
    158 			p.Get()
    159 		}
    160 	})
    161 }
    162 
    163 func BenchmarkPoolOverflow(b *testing.B) {
    164 	var p Pool
    165 	b.RunParallel(func(pb *testing.PB) {
    166 		for pb.Next() {
    167 			for b := 0; b < 100; b++ {
    168 				p.Put(1)
    169 			}
    170 			for b := 0; b < 100; b++ {
    171 				p.Get()
    172 			}
    173 		}
    174 	})
    175 }
    176