Source file
src/sync/waitgroup_test.go
Documentation: sync
1
2
3
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
38 }
39 }
40
41 func TestWaitGroup(t *testing.T) {
42 wg1 := &WaitGroup{}
43 wg2 := &WaitGroup{}
44
45
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
72
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
81
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
102
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)
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
152
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
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
191 for i := 0; i < 1000; i++ {
192 wg := &WaitGroup{}
193 n := new(int32)
194
195 wg.Add(1)
196 go func() {
197 atomic.AddInt32(n, 1)
198 wg.Done()
199 }()
200
201 wg.Add(1)
202 go func() {
203 atomic.AddInt32(n, 1)
204 wg.Done()
205 }()
206
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 }
302
View as plain text