github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/sync/waitgroup_test.go (about) 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 5 package sync_test 6 7 import ( 8 "internal/race" 9 "runtime" 10 . "sync" 11 "sync/atomic" 12 "testing" 13 ) 14 15 func testWaitGroup(t *testing.T, wg1 *WaitGroup, wg2 *WaitGroup) { 16 n := 16 17 wg1.Add(n) 18 wg2.Add(n) 19 exited := make(chan bool, n) 20 for i := 0; i != n; i++ { 21 go func(i int) { 22 wg1.Done() 23 wg2.Wait() 24 exited <- true 25 }(i) 26 } 27 wg1.Wait() 28 for i := 0; i != n; i++ { 29 select { 30 case <-exited: 31 t.Fatal("WaitGroup released group too soon") 32 default: 33 } 34 wg2.Done() 35 } 36 for i := 0; i != n; i++ { 37 <-exited // Will block if barrier fails to unlock someone. 38 } 39 } 40 41 func TestWaitGroup(t *testing.T) { 42 wg1 := &WaitGroup{} 43 wg2 := &WaitGroup{} 44 45 // Run the same test a few times to ensure barrier is in a proper state. 46 for i := 0; i != 8; i++ { 47 testWaitGroup(t, wg1, wg2) 48 } 49 } 50 51 func knownRacy(t *testing.T) { 52 if race.Enabled { 53 t.Skip("skipping known-racy test under the race detector") 54 } 55 } 56 57 func TestWaitGroupMisuse(t *testing.T) { 58 defer func() { 59 err := recover() 60 if err != "sync: negative WaitGroup counter" { 61 t.Fatalf("Unexpected panic: %#v", err) 62 } 63 }() 64 wg := &WaitGroup{} 65 wg.Add(1) 66 wg.Done() 67 wg.Done() 68 t.Fatal("Should panic") 69 } 70 71 func TestWaitGroupMisuse2(t *testing.T) { 72 knownRacy(t) 73 if testing.Short() { 74 t.Skip("skipping flaky test in short mode; see issue 11443") 75 } 76 if runtime.NumCPU() <= 2 { 77 t.Skip("NumCPU<=2, skipping: this test requires parallelism") 78 } 79 defer func() { 80 err := recover() 81 if err != "sync: negative WaitGroup counter" && 82 err != "sync: WaitGroup misuse: Add called concurrently with Wait" && 83 err != "sync: WaitGroup is reused before previous Wait has returned" { 84 t.Fatalf("Unexpected panic: %#v", err) 85 } 86 }() 87 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 88 done := make(chan interface{}, 2) 89 // The detection is opportunistically, so we want it to panic 90 // at least in one run out of a million. 91 for i := 0; i < 1e6; i++ { 92 var wg WaitGroup 93 wg.Add(1) 94 go func() { 95 defer func() { 96 done <- recover() 97 }() 98 wg.Wait() 99 }() 100 go func() { 101 defer func() { 102 done <- recover() 103 }() 104 wg.Add(1) // This is the bad guy. 105 wg.Done() 106 }() 107 wg.Done() 108 for j := 0; j < 2; j++ { 109 if err := <-done; err != nil { 110 panic(err) 111 } 112 } 113 } 114 t.Fatal("Should panic") 115 } 116 117 func TestWaitGroupMisuse3(t *testing.T) { 118 knownRacy(t) 119 if runtime.NumCPU() <= 1 { 120 t.Skip("NumCPU==1, skipping: this test requires parallelism") 121 } 122 defer func() { 123 err := recover() 124 if err != "sync: negative WaitGroup counter" && 125 err != "sync: WaitGroup misuse: Add called concurrently with Wait" && 126 err != "sync: WaitGroup is reused before previous Wait has returned" { 127 t.Fatalf("Unexpected panic: %#v", err) 128 } 129 }() 130 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 131 done := make(chan interface{}, 1) 132 // The detection is opportunistically, so we want it to panic 133 // at least in one run out of a million. 134 for i := 0; i < 1e6; i++ { 135 var wg WaitGroup 136 wg.Add(1) 137 go func() { 138 wg.Done() 139 }() 140 go func() { 141 defer func() { 142 done <- recover() 143 }() 144 wg.Wait() 145 // Start reusing the wg before waiting for the Wait below to return. 146 wg.Add(1) 147 go func() { 148 wg.Done() 149 }() 150 wg.Wait() 151 }() 152 wg.Wait() 153 if err := <-done; err != nil { 154 panic(err) 155 } 156 } 157 t.Fatal("Should panic") 158 } 159 160 func TestWaitGroupRace(t *testing.T) { 161 // Run this test for about 1ms. 162 for i := 0; i < 1000; i++ { 163 wg := &WaitGroup{} 164 n := new(int32) 165 // spawn goroutine 1 166 wg.Add(1) 167 go func() { 168 atomic.AddInt32(n, 1) 169 wg.Done() 170 }() 171 // spawn goroutine 2 172 wg.Add(1) 173 go func() { 174 atomic.AddInt32(n, 1) 175 wg.Done() 176 }() 177 // Wait for goroutine 1 and 2 178 wg.Wait() 179 if atomic.LoadInt32(n) != 2 { 180 t.Fatal("Spurious wakeup from Wait") 181 } 182 } 183 } 184 185 func TestWaitGroupAlign(t *testing.T) { 186 type X struct { 187 x byte 188 wg WaitGroup 189 } 190 var x X 191 x.wg.Add(1) 192 go func(x *X) { 193 x.wg.Done() 194 }(&x) 195 x.wg.Wait() 196 } 197 198 func BenchmarkWaitGroupUncontended(b *testing.B) { 199 type PaddedWaitGroup struct { 200 WaitGroup 201 pad [128]uint8 202 } 203 b.RunParallel(func(pb *testing.PB) { 204 var wg PaddedWaitGroup 205 for pb.Next() { 206 wg.Add(1) 207 wg.Done() 208 wg.Wait() 209 } 210 }) 211 } 212 213 func benchmarkWaitGroupAddDone(b *testing.B, localWork int) { 214 var wg WaitGroup 215 b.RunParallel(func(pb *testing.PB) { 216 foo := 0 217 for pb.Next() { 218 wg.Add(1) 219 for i := 0; i < localWork; i++ { 220 foo *= 2 221 foo /= 2 222 } 223 wg.Done() 224 } 225 _ = foo 226 }) 227 } 228 229 func BenchmarkWaitGroupAddDone(b *testing.B) { 230 benchmarkWaitGroupAddDone(b, 0) 231 } 232 233 func BenchmarkWaitGroupAddDoneWork(b *testing.B) { 234 benchmarkWaitGroupAddDone(b, 100) 235 } 236 237 func benchmarkWaitGroupWait(b *testing.B, localWork int) { 238 var wg WaitGroup 239 b.RunParallel(func(pb *testing.PB) { 240 foo := 0 241 for pb.Next() { 242 wg.Wait() 243 for i := 0; i < localWork; i++ { 244 foo *= 2 245 foo /= 2 246 } 247 } 248 _ = foo 249 }) 250 } 251 252 func BenchmarkWaitGroupWait(b *testing.B) { 253 benchmarkWaitGroupWait(b, 0) 254 } 255 256 func BenchmarkWaitGroupWaitWork(b *testing.B) { 257 benchmarkWaitGroupWait(b, 100) 258 } 259 260 func BenchmarkWaitGroupActuallyWait(b *testing.B) { 261 b.ReportAllocs() 262 b.RunParallel(func(pb *testing.PB) { 263 for pb.Next() { 264 var wg WaitGroup 265 wg.Add(1) 266 go func() { 267 wg.Done() 268 }() 269 wg.Wait() 270 } 271 }) 272 }