1// Copyright 2012 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 race_test
6
7import (
8	"runtime"
9	"sync"
10	"testing"
11	"time"
12)
13
14func TestNoRaceWaitGroup(t *testing.T) {
15	var x int
16	_ = x
17	var wg sync.WaitGroup
18	n := 1
19	for i := 0; i < n; i++ {
20		wg.Add(1)
21		j := i
22		go func() {
23			x = j
24			wg.Done()
25		}()
26	}
27	wg.Wait()
28}
29
30func TestRaceWaitGroup(t *testing.T) {
31	var x int
32	_ = x
33	var wg sync.WaitGroup
34	n := 2
35	for i := 0; i < n; i++ {
36		wg.Add(1)
37		j := i
38		go func() {
39			x = j
40			wg.Done()
41		}()
42	}
43	wg.Wait()
44}
45
46func TestNoRaceWaitGroup2(t *testing.T) {
47	var x int
48	_ = x
49	var wg sync.WaitGroup
50	wg.Add(1)
51	go func() {
52		x = 1
53		wg.Done()
54	}()
55	wg.Wait()
56	x = 2
57}
58
59// incrementing counter in Add and locking wg's mutex
60func TestRaceWaitGroupAsMutex(t *testing.T) {
61	var x int
62	_ = x
63	var wg sync.WaitGroup
64	c := make(chan bool, 2)
65	go func() {
66		wg.Wait()
67		time.Sleep(100 * time.Millisecond)
68		wg.Add(+1)
69		x = 1
70		wg.Add(-1)
71		c <- true
72	}()
73	go func() {
74		wg.Wait()
75		time.Sleep(100 * time.Millisecond)
76		wg.Add(+1)
77		x = 2
78		wg.Add(-1)
79		c <- true
80	}()
81	<-c
82	<-c
83}
84
85// Incorrect usage: Add is too late.
86func TestRaceWaitGroupWrongWait(t *testing.T) {
87	c := make(chan bool, 2)
88	var x int
89	_ = x
90	var wg sync.WaitGroup
91	go func() {
92		wg.Add(1)
93		runtime.Gosched()
94		x = 1
95		wg.Done()
96		c <- true
97	}()
98	go func() {
99		wg.Add(1)
100		runtime.Gosched()
101		x = 2
102		wg.Done()
103		c <- true
104	}()
105	wg.Wait()
106	<-c
107	<-c
108}
109
110func TestRaceWaitGroupWrongAdd(t *testing.T) {
111	c := make(chan bool, 2)
112	var wg sync.WaitGroup
113	go func() {
114		wg.Add(1)
115		time.Sleep(100 * time.Millisecond)
116		wg.Done()
117		c <- true
118	}()
119	go func() {
120		wg.Add(1)
121		time.Sleep(100 * time.Millisecond)
122		wg.Done()
123		c <- true
124	}()
125	time.Sleep(50 * time.Millisecond)
126	wg.Wait()
127	<-c
128	<-c
129}
130
131func TestNoRaceWaitGroupMultipleWait(t *testing.T) {
132	c := make(chan bool, 2)
133	var wg sync.WaitGroup
134	go func() {
135		wg.Wait()
136		c <- true
137	}()
138	go func() {
139		wg.Wait()
140		c <- true
141	}()
142	wg.Wait()
143	<-c
144	<-c
145}
146
147func TestNoRaceWaitGroupMultipleWait2(t *testing.T) {
148	c := make(chan bool, 2)
149	var wg sync.WaitGroup
150	wg.Add(2)
151	go func() {
152		wg.Done()
153		wg.Wait()
154		c <- true
155	}()
156	go func() {
157		wg.Done()
158		wg.Wait()
159		c <- true
160	}()
161	wg.Wait()
162	<-c
163	<-c
164}
165
166func TestNoRaceWaitGroupMultipleWait3(t *testing.T) {
167	const P = 3
168	var data [P]int
169	done := make(chan bool, P)
170	var wg sync.WaitGroup
171	wg.Add(P)
172	for p := 0; p < P; p++ {
173		go func(p int) {
174			data[p] = 42
175			wg.Done()
176		}(p)
177	}
178	for p := 0; p < P; p++ {
179		go func() {
180			wg.Wait()
181			for p1 := 0; p1 < P; p1++ {
182				_ = data[p1]
183			}
184			done <- true
185		}()
186	}
187	for p := 0; p < P; p++ {
188		<-done
189	}
190}
191
192// Correct usage but still a race
193func TestRaceWaitGroup2(t *testing.T) {
194	var x int
195	_ = x
196	var wg sync.WaitGroup
197	wg.Add(2)
198	go func() {
199		x = 1
200		wg.Done()
201	}()
202	go func() {
203		x = 2
204		wg.Done()
205	}()
206	wg.Wait()
207}
208
209func TestNoRaceWaitGroupPanicRecover(t *testing.T) {
210	var x int
211	_ = x
212	var wg sync.WaitGroup
213	defer func() {
214		err := recover()
215		if err != "sync: negative WaitGroup counter" {
216			t.Fatalf("Unexpected panic: %#v", err)
217		}
218		x = 2
219	}()
220	x = 1
221	wg.Add(-1)
222}
223
224// TODO: this is actually a panic-synchronization test, not a
225// WaitGroup test. Move it to another *_test file
226// Is it possible to get a race by synchronization via panic?
227func TestNoRaceWaitGroupPanicRecover2(t *testing.T) {
228	var x int
229	_ = x
230	var wg sync.WaitGroup
231	ch := make(chan bool, 1)
232	var f func() = func() {
233		x = 2
234		ch <- true
235	}
236	go func() {
237		defer func() {
238			err := recover()
239			if err != "sync: negative WaitGroup counter" {
240			}
241			go f()
242		}()
243		x = 1
244		wg.Add(-1)
245	}()
246
247	<-ch
248}
249
250func TestNoRaceWaitGroupTransitive(t *testing.T) {
251	x, y := 0, 0
252	var wg sync.WaitGroup
253	wg.Add(2)
254	go func() {
255		x = 42
256		wg.Done()
257	}()
258	go func() {
259		time.Sleep(1e7)
260		y = 42
261		wg.Done()
262	}()
263	wg.Wait()
264	_ = x
265	_ = y
266}
267
268func TestNoRaceWaitGroupReuse(t *testing.T) {
269	const P = 3
270	var data [P]int
271	var wg sync.WaitGroup
272	for try := 0; try < 3; try++ {
273		wg.Add(P)
274		for p := 0; p < P; p++ {
275			go func(p int) {
276				data[p]++
277				wg.Done()
278			}(p)
279		}
280		wg.Wait()
281		for p := 0; p < P; p++ {
282			data[p]++
283		}
284	}
285}
286
287func TestNoRaceWaitGroupReuse2(t *testing.T) {
288	const P = 3
289	var data [P]int
290	var wg sync.WaitGroup
291	for try := 0; try < 3; try++ {
292		wg.Add(P)
293		for p := 0; p < P; p++ {
294			go func(p int) {
295				data[p]++
296				wg.Done()
297			}(p)
298		}
299		done := make(chan bool)
300		go func() {
301			wg.Wait()
302			for p := 0; p < P; p++ {
303				data[p]++
304			}
305			done <- true
306		}()
307		wg.Wait()
308		<-done
309		for p := 0; p < P; p++ {
310			data[p]++
311		}
312	}
313}
314
315func TestRaceWaitGroupReuse(t *testing.T) {
316	const P = 3
317	const T = 3
318	done := make(chan bool, T)
319	var wg sync.WaitGroup
320	for try := 0; try < T; try++ {
321		var data [P]int
322		wg.Add(P)
323		for p := 0; p < P; p++ {
324			go func(p int) {
325				time.Sleep(50 * time.Millisecond)
326				data[p]++
327				wg.Done()
328			}(p)
329		}
330		go func() {
331			wg.Wait()
332			for p := 0; p < P; p++ {
333				data[p]++
334			}
335			done <- true
336		}()
337		time.Sleep(100 * time.Millisecond)
338		wg.Wait()
339	}
340	for try := 0; try < T; try++ {
341		<-done
342	}
343}
344
345func TestNoRaceWaitGroupConcurrentAdd(t *testing.T) {
346	const P = 4
347	waiting := make(chan bool, P)
348	var wg sync.WaitGroup
349	for p := 0; p < P; p++ {
350		go func() {
351			wg.Add(1)
352			waiting <- true
353			wg.Done()
354		}()
355	}
356	for p := 0; p < P; p++ {
357		<-waiting
358	}
359	wg.Wait()
360}
361