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