1// Copyright 2011 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 5package sync_test 6 7import ( 8 . "sync" 9 "sync/atomic" 10 "testing" 11) 12 13func testWaitGroup(t *testing.T, wg1 *WaitGroup, wg2 *WaitGroup) { 14 n := 16 15 wg1.Add(n) 16 wg2.Add(n) 17 exited := make(chan bool, n) 18 for i := 0; i != n; i++ { 19 go func() { 20 wg1.Done() 21 wg2.Wait() 22 exited <- true 23 }() 24 } 25 wg1.Wait() 26 for i := 0; i != n; i++ { 27 select { 28 case <-exited: 29 t.Fatal("WaitGroup released group too soon") 30 default: 31 } 32 wg2.Done() 33 } 34 for i := 0; i != n; i++ { 35 <-exited // Will block if barrier fails to unlock someone. 36 } 37} 38 39func TestWaitGroup(t *testing.T) { 40 wg1 := &WaitGroup{} 41 wg2 := &WaitGroup{} 42 43 // Run the same test a few times to ensure barrier is in a proper state. 44 for i := 0; i != 8; i++ { 45 testWaitGroup(t, wg1, wg2) 46 } 47} 48 49func TestWaitGroupMisuse(t *testing.T) { 50 defer func() { 51 err := recover() 52 if err != "sync: negative WaitGroup counter" { 53 t.Fatalf("Unexpected panic: %#v", err) 54 } 55 }() 56 wg := &WaitGroup{} 57 wg.Add(1) 58 wg.Done() 59 wg.Done() 60 t.Fatal("Should panic") 61} 62 63func TestWaitGroupRace(t *testing.T) { 64 // Run this test for about 1ms. 65 for i := 0; i < 1000; i++ { 66 wg := &WaitGroup{} 67 n := new(int32) 68 // spawn goroutine 1 69 wg.Add(1) 70 go func() { 71 atomic.AddInt32(n, 1) 72 wg.Done() 73 }() 74 // spawn goroutine 2 75 wg.Add(1) 76 go func() { 77 atomic.AddInt32(n, 1) 78 wg.Done() 79 }() 80 // Wait for goroutine 1 and 2 81 wg.Wait() 82 if atomic.LoadInt32(n) != 2 { 83 t.Fatal("Spurious wakeup from Wait") 84 } 85 } 86} 87 88func TestWaitGroupAlign(t *testing.T) { 89 type X struct { 90 x byte 91 wg WaitGroup 92 } 93 var x X 94 x.wg.Add(1) 95 go func(x *X) { 96 x.wg.Done() 97 }(&x) 98 x.wg.Wait() 99} 100 101func BenchmarkWaitGroupUncontended(b *testing.B) { 102 type PaddedWaitGroup struct { 103 WaitGroup 104 pad [128]uint8 105 } 106 b.RunParallel(func(pb *testing.PB) { 107 var wg PaddedWaitGroup 108 for pb.Next() { 109 wg.Add(1) 110 wg.Done() 111 wg.Wait() 112 } 113 }) 114} 115 116func benchmarkWaitGroupAddDone(b *testing.B, localWork int) { 117 var wg WaitGroup 118 b.RunParallel(func(pb *testing.PB) { 119 foo := 0 120 for pb.Next() { 121 wg.Add(1) 122 for i := 0; i < localWork; i++ { 123 foo *= 2 124 foo /= 2 125 } 126 wg.Done() 127 } 128 _ = foo 129 }) 130} 131 132func BenchmarkWaitGroupAddDone(b *testing.B) { 133 benchmarkWaitGroupAddDone(b, 0) 134} 135 136func BenchmarkWaitGroupAddDoneWork(b *testing.B) { 137 benchmarkWaitGroupAddDone(b, 100) 138} 139 140func benchmarkWaitGroupWait(b *testing.B, localWork int) { 141 var wg WaitGroup 142 b.RunParallel(func(pb *testing.PB) { 143 foo := 0 144 for pb.Next() { 145 wg.Wait() 146 for i := 0; i < localWork; i++ { 147 foo *= 2 148 foo /= 2 149 } 150 } 151 _ = foo 152 }) 153} 154 155func BenchmarkWaitGroupWait(b *testing.B) { 156 benchmarkWaitGroupWait(b, 0) 157} 158 159func BenchmarkWaitGroupWaitWork(b *testing.B) { 160 benchmarkWaitGroupWait(b, 100) 161} 162 163func BenchmarkWaitGroupActuallyWait(b *testing.B) { 164 b.ReportAllocs() 165 b.RunParallel(func(pb *testing.PB) { 166 for pb.Next() { 167 var wg WaitGroup 168 wg.Add(1) 169 go func() { 170 wg.Done() 171 }() 172 wg.Wait() 173 } 174 }) 175} 176