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 "internal/race" 9 "runtime" 10 . "sync" 11 "sync/atomic" 12 "testing" 13) 14 15func 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() { 22 wg1.Done() 23 wg2.Wait() 24 exited <- true 25 }() 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 41func 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 51func knownRacy(t *testing.T) { 52 if race.Enabled { 53 t.Skip("skipping known-racy test under the race detector") 54 } 55} 56 57func 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// pollUntilEqual blocks until v, loaded atomically, is 72// equal to the target. 73func pollUntilEqual(v *uint32, target uint32) { 74 for { 75 for i := 0; i < 1e3; i++ { 76 if atomic.LoadUint32(v) == target { 77 return 78 } 79 } 80 // yield to avoid deadlock with the garbage collector 81 // see issue #20072 82 runtime.Gosched() 83 } 84} 85 86func TestWaitGroupMisuse2(t *testing.T) { 87 knownRacy(t) 88 if runtime.NumCPU() <= 4 { 89 t.Skip("NumCPU<=4, skipping: this test requires parallelism") 90 } 91 defer func() { 92 err := recover() 93 if err != "sync: negative WaitGroup counter" && 94 err != "sync: WaitGroup misuse: Add called concurrently with Wait" && 95 err != "sync: WaitGroup is reused before previous Wait has returned" { 96 t.Fatalf("Unexpected panic: %#v", err) 97 } 98 }() 99 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 100 done := make(chan interface{}, 2) 101 // The detection is opportunistic, so we want it to panic 102 // at least in one run out of a million. 103 for i := 0; i < 1e6; i++ { 104 var wg WaitGroup 105 var here uint32 106 wg.Add(1) 107 go func() { 108 defer func() { 109 done <- recover() 110 }() 111 atomic.AddUint32(&here, 1) 112 pollUntilEqual(&here, 3) 113 wg.Wait() 114 }() 115 go func() { 116 defer func() { 117 done <- recover() 118 }() 119 atomic.AddUint32(&here, 1) 120 pollUntilEqual(&here, 3) 121 wg.Add(1) // This is the bad guy. 122 wg.Done() 123 }() 124 atomic.AddUint32(&here, 1) 125 pollUntilEqual(&here, 3) 126 wg.Done() 127 for j := 0; j < 2; j++ { 128 if err := <-done; err != nil { 129 panic(err) 130 } 131 } 132 } 133 t.Fatal("Should panic") 134} 135 136func TestWaitGroupMisuse3(t *testing.T) { 137 knownRacy(t) 138 if runtime.NumCPU() <= 1 { 139 t.Skip("NumCPU==1, skipping: this test requires parallelism") 140 } 141 defer func() { 142 err := recover() 143 if err != "sync: negative WaitGroup counter" && 144 err != "sync: WaitGroup misuse: Add called concurrently with Wait" && 145 err != "sync: WaitGroup is reused before previous Wait has returned" { 146 t.Fatalf("Unexpected panic: %#v", err) 147 } 148 }() 149 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 150 done := make(chan interface{}, 2) 151 // The detection is opportunistically, so we want it to panic 152 // at least in one run out of a million. 153 for i := 0; i < 1e6; i++ { 154 var wg WaitGroup 155 wg.Add(1) 156 go func() { 157 defer func() { 158 done <- recover() 159 }() 160 wg.Done() 161 }() 162 go func() { 163 defer func() { 164 done <- recover() 165 }() 166 wg.Wait() 167 // Start reusing the wg before waiting for the Wait below to return. 168 wg.Add(1) 169 go func() { 170 wg.Done() 171 }() 172 wg.Wait() 173 }() 174 wg.Wait() 175 for j := 0; j < 2; j++ { 176 if err := <-done; err != nil { 177 panic(err) 178 } 179 } 180 } 181 t.Fatal("Should panic") 182} 183 184func TestWaitGroupRace(t *testing.T) { 185 // Run this test for about 1ms. 186 for i := 0; i < 1000; i++ { 187 wg := &WaitGroup{} 188 n := new(int32) 189 // spawn goroutine 1 190 wg.Add(1) 191 go func() { 192 atomic.AddInt32(n, 1) 193 wg.Done() 194 }() 195 // spawn goroutine 2 196 wg.Add(1) 197 go func() { 198 atomic.AddInt32(n, 1) 199 wg.Done() 200 }() 201 // Wait for goroutine 1 and 2 202 wg.Wait() 203 if atomic.LoadInt32(n) != 2 { 204 t.Fatal("Spurious wakeup from Wait") 205 } 206 } 207} 208 209func TestWaitGroupAlign(t *testing.T) { 210 type X struct { 211 x byte 212 wg WaitGroup 213 } 214 var x X 215 x.wg.Add(1) 216 go func(x *X) { 217 x.wg.Done() 218 }(&x) 219 x.wg.Wait() 220} 221 222func BenchmarkWaitGroupUncontended(b *testing.B) { 223 type PaddedWaitGroup struct { 224 WaitGroup 225 pad [128]uint8 226 } 227 b.RunParallel(func(pb *testing.PB) { 228 var wg PaddedWaitGroup 229 for pb.Next() { 230 wg.Add(1) 231 wg.Done() 232 wg.Wait() 233 } 234 }) 235} 236 237func benchmarkWaitGroupAddDone(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.Add(1) 243 for i := 0; i < localWork; i++ { 244 foo *= 2 245 foo /= 2 246 } 247 wg.Done() 248 } 249 _ = foo 250 }) 251} 252 253func BenchmarkWaitGroupAddDone(b *testing.B) { 254 benchmarkWaitGroupAddDone(b, 0) 255} 256 257func BenchmarkWaitGroupAddDoneWork(b *testing.B) { 258 benchmarkWaitGroupAddDone(b, 100) 259} 260 261func benchmarkWaitGroupWait(b *testing.B, localWork int) { 262 var wg WaitGroup 263 b.RunParallel(func(pb *testing.PB) { 264 foo := 0 265 for pb.Next() { 266 wg.Wait() 267 for i := 0; i < localWork; i++ { 268 foo *= 2 269 foo /= 2 270 } 271 } 272 _ = foo 273 }) 274} 275 276func BenchmarkWaitGroupWait(b *testing.B) { 277 benchmarkWaitGroupWait(b, 0) 278} 279 280func BenchmarkWaitGroupWaitWork(b *testing.B) { 281 benchmarkWaitGroupWait(b, 100) 282} 283 284func BenchmarkWaitGroupActuallyWait(b *testing.B) { 285 b.ReportAllocs() 286 b.RunParallel(func(pb *testing.PB) { 287 for pb.Next() { 288 var wg WaitGroup 289 wg.Add(1) 290 go func() { 291 wg.Done() 292 }() 293 wg.Wait() 294 } 295 }) 296} 297