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