github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/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() { 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 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 // pollUntilEqual blocks until v, loaded atomically, is 72 // equal to the target. 73 func 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 86 func 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 136 func 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{}, 3) 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 go func() { 175 defer func() { 176 done <- recover() 177 }() 178 wg.Wait() 179 }() 180 for j := 0; j < 3; j++ { 181 if err := <-done; err != nil { 182 panic(err) 183 } 184 } 185 } 186 t.Fatal("Should panic") 187 } 188 189 func TestWaitGroupRace(t *testing.T) { 190 // Run this test for about 1ms. 191 for i := 0; i < 1000; i++ { 192 wg := &WaitGroup{} 193 n := new(int32) 194 // spawn goroutine 1 195 wg.Add(1) 196 go func() { 197 atomic.AddInt32(n, 1) 198 wg.Done() 199 }() 200 // spawn goroutine 2 201 wg.Add(1) 202 go func() { 203 atomic.AddInt32(n, 1) 204 wg.Done() 205 }() 206 // Wait for goroutine 1 and 2 207 wg.Wait() 208 if atomic.LoadInt32(n) != 2 { 209 t.Fatal("Spurious wakeup from Wait") 210 } 211 } 212 } 213 214 func TestWaitGroupAlign(t *testing.T) { 215 type X struct { 216 x byte 217 wg WaitGroup 218 } 219 var x X 220 x.wg.Add(1) 221 go func(x *X) { 222 x.wg.Done() 223 }(&x) 224 x.wg.Wait() 225 } 226 227 func BenchmarkWaitGroupUncontended(b *testing.B) { 228 type PaddedWaitGroup struct { 229 WaitGroup 230 pad [128]uint8 231 } 232 b.RunParallel(func(pb *testing.PB) { 233 var wg PaddedWaitGroup 234 for pb.Next() { 235 wg.Add(1) 236 wg.Done() 237 wg.Wait() 238 } 239 }) 240 } 241 242 func benchmarkWaitGroupAddDone(b *testing.B, localWork int) { 243 var wg WaitGroup 244 b.RunParallel(func(pb *testing.PB) { 245 foo := 0 246 for pb.Next() { 247 wg.Add(1) 248 for i := 0; i < localWork; i++ { 249 foo *= 2 250 foo /= 2 251 } 252 wg.Done() 253 } 254 _ = foo 255 }) 256 } 257 258 func BenchmarkWaitGroupAddDone(b *testing.B) { 259 benchmarkWaitGroupAddDone(b, 0) 260 } 261 262 func BenchmarkWaitGroupAddDoneWork(b *testing.B) { 263 benchmarkWaitGroupAddDone(b, 100) 264 } 265 266 func benchmarkWaitGroupWait(b *testing.B, localWork int) { 267 var wg WaitGroup 268 b.RunParallel(func(pb *testing.PB) { 269 foo := 0 270 for pb.Next() { 271 wg.Wait() 272 for i := 0; i < localWork; i++ { 273 foo *= 2 274 foo /= 2 275 } 276 } 277 _ = foo 278 }) 279 } 280 281 func BenchmarkWaitGroupWait(b *testing.B) { 282 benchmarkWaitGroupWait(b, 0) 283 } 284 285 func BenchmarkWaitGroupWaitWork(b *testing.B) { 286 benchmarkWaitGroupWait(b, 100) 287 } 288 289 func BenchmarkWaitGroupActuallyWait(b *testing.B) { 290 b.ReportAllocs() 291 b.RunParallel(func(pb *testing.PB) { 292 for pb.Next() { 293 var wg WaitGroup 294 wg.Add(1) 295 go func() { 296 wg.Done() 297 }() 298 wg.Wait() 299 } 300 }) 301 }