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	. "sync"
9	"sync/atomic"
10	"testing"
11)
12
13func testWaitGroup(t *testing.T, wg1 *WaitGroup, wg2 *WaitGroup) {
14	n := 16
15	wg1.Add(n)
16	wg2.Add(n)
17	exited := make(chan bool, n)
18	for i := 0; i != n; i++ {
19		go func() {
20			wg1.Done()
21			wg2.Wait()
22			exited <- true
23		}()
24	}
25	wg1.Wait()
26	for i := 0; i != n; i++ {
27		select {
28		case <-exited:
29			t.Fatal("WaitGroup released group too soon")
30		default:
31		}
32		wg2.Done()
33	}
34	for i := 0; i != n; i++ {
35		<-exited // Will block if barrier fails to unlock someone.
36	}
37}
38
39func TestWaitGroup(t *testing.T) {
40	wg1 := &WaitGroup{}
41	wg2 := &WaitGroup{}
42
43	// Run the same test a few times to ensure barrier is in a proper state.
44	for i := 0; i != 8; i++ {
45		testWaitGroup(t, wg1, wg2)
46	}
47}
48
49func TestWaitGroupMisuse(t *testing.T) {
50	defer func() {
51		err := recover()
52		if err != "sync: negative WaitGroup counter" {
53			t.Fatalf("Unexpected panic: %#v", err)
54		}
55	}()
56	wg := &WaitGroup{}
57	wg.Add(1)
58	wg.Done()
59	wg.Done()
60	t.Fatal("Should panic")
61}
62
63func TestWaitGroupRace(t *testing.T) {
64	// Run this test for about 1ms.
65	for i := 0; i < 1000; i++ {
66		wg := &WaitGroup{}
67		n := new(int32)
68		// spawn goroutine 1
69		wg.Add(1)
70		go func() {
71			atomic.AddInt32(n, 1)
72			wg.Done()
73		}()
74		// spawn goroutine 2
75		wg.Add(1)
76		go func() {
77			atomic.AddInt32(n, 1)
78			wg.Done()
79		}()
80		// Wait for goroutine 1 and 2
81		wg.Wait()
82		if atomic.LoadInt32(n) != 2 {
83			t.Fatal("Spurious wakeup from Wait")
84		}
85	}
86}
87
88func TestWaitGroupAlign(t *testing.T) {
89	type X struct {
90		x  byte
91		wg WaitGroup
92	}
93	var x X
94	x.wg.Add(1)
95	go func(x *X) {
96		x.wg.Done()
97	}(&x)
98	x.wg.Wait()
99}
100
101func BenchmarkWaitGroupUncontended(b *testing.B) {
102	type PaddedWaitGroup struct {
103		WaitGroup
104		pad [128]uint8
105	}
106	b.RunParallel(func(pb *testing.PB) {
107		var wg PaddedWaitGroup
108		for pb.Next() {
109			wg.Add(1)
110			wg.Done()
111			wg.Wait()
112		}
113	})
114}
115
116func benchmarkWaitGroupAddDone(b *testing.B, localWork int) {
117	var wg WaitGroup
118	b.RunParallel(func(pb *testing.PB) {
119		foo := 0
120		for pb.Next() {
121			wg.Add(1)
122			for i := 0; i < localWork; i++ {
123				foo *= 2
124				foo /= 2
125			}
126			wg.Done()
127		}
128		_ = foo
129	})
130}
131
132func BenchmarkWaitGroupAddDone(b *testing.B) {
133	benchmarkWaitGroupAddDone(b, 0)
134}
135
136func BenchmarkWaitGroupAddDoneWork(b *testing.B) {
137	benchmarkWaitGroupAddDone(b, 100)
138}
139
140func benchmarkWaitGroupWait(b *testing.B, localWork int) {
141	var wg WaitGroup
142	b.RunParallel(func(pb *testing.PB) {
143		foo := 0
144		for pb.Next() {
145			wg.Wait()
146			for i := 0; i < localWork; i++ {
147				foo *= 2
148				foo /= 2
149			}
150		}
151		_ = foo
152	})
153}
154
155func BenchmarkWaitGroupWait(b *testing.B) {
156	benchmarkWaitGroupWait(b, 0)
157}
158
159func BenchmarkWaitGroupWaitWork(b *testing.B) {
160	benchmarkWaitGroupWait(b, 100)
161}
162
163func BenchmarkWaitGroupActuallyWait(b *testing.B) {
164	b.ReportAllocs()
165	b.RunParallel(func(pb *testing.PB) {
166		for pb.Next() {
167			var wg WaitGroup
168			wg.Add(1)
169			go func() {
170				wg.Done()
171			}()
172			wg.Wait()
173		}
174	})
175}
176