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	"reflect"
9	"runtime"
10	. "sync"
11	"testing"
12)
13
14func TestCondSignal(t *testing.T) {
15	var m Mutex
16	c := NewCond(&m)
17	n := 2
18	running := make(chan bool, n)
19	awake := make(chan bool, n)
20	for i := 0; i < n; i++ {
21		go func() {
22			m.Lock()
23			running <- true
24			c.Wait()
25			awake <- true
26			m.Unlock()
27		}()
28	}
29	for i := 0; i < n; i++ {
30		<-running // Wait for everyone to run.
31	}
32	for n > 0 {
33		select {
34		case <-awake:
35			t.Fatal("goroutine not asleep")
36		default:
37		}
38		m.Lock()
39		c.Signal()
40		m.Unlock()
41		<-awake // Will deadlock if no goroutine wakes up
42		select {
43		case <-awake:
44			t.Fatal("too many goroutines awake")
45		default:
46		}
47		n--
48	}
49	c.Signal()
50}
51
52func TestCondSignalGenerations(t *testing.T) {
53	var m Mutex
54	c := NewCond(&m)
55	n := 100
56	running := make(chan bool, n)
57	awake := make(chan int, n)
58	for i := 0; i < n; i++ {
59		go func(i int) {
60			m.Lock()
61			running <- true
62			c.Wait()
63			awake <- i
64			m.Unlock()
65		}(i)
66		if i > 0 {
67			a := <-awake
68			if a != i-1 {
69				t.Fatalf("wrong goroutine woke up: want %d, got %d", i-1, a)
70			}
71		}
72		<-running
73		m.Lock()
74		c.Signal()
75		m.Unlock()
76	}
77}
78
79func TestCondBroadcast(t *testing.T) {
80	var m Mutex
81	c := NewCond(&m)
82	n := 200
83	running := make(chan int, n)
84	awake := make(chan int, n)
85	exit := false
86	for i := 0; i < n; i++ {
87		go func(g int) {
88			m.Lock()
89			for !exit {
90				running <- g
91				c.Wait()
92				awake <- g
93			}
94			m.Unlock()
95		}(i)
96	}
97	for i := 0; i < n; i++ {
98		for i := 0; i < n; i++ {
99			<-running // Will deadlock unless n are running.
100		}
101		if i == n-1 {
102			m.Lock()
103			exit = true
104			m.Unlock()
105		}
106		select {
107		case <-awake:
108			t.Fatal("goroutine not asleep")
109		default:
110		}
111		m.Lock()
112		c.Broadcast()
113		m.Unlock()
114		seen := make([]bool, n)
115		for i := 0; i < n; i++ {
116			g := <-awake
117			if seen[g] {
118				t.Fatal("goroutine woke up twice")
119			}
120			seen[g] = true
121		}
122	}
123	select {
124	case <-running:
125		t.Fatal("goroutine did not exit")
126	default:
127	}
128	c.Broadcast()
129}
130
131func TestRace(t *testing.T) {
132	x := 0
133	c := NewCond(&Mutex{})
134	done := make(chan bool)
135	go func() {
136		c.L.Lock()
137		x = 1
138		c.Wait()
139		if x != 2 {
140			t.Error("want 2")
141		}
142		x = 3
143		c.Signal()
144		c.L.Unlock()
145		done <- true
146	}()
147	go func() {
148		c.L.Lock()
149		for {
150			if x == 1 {
151				x = 2
152				c.Signal()
153				break
154			}
155			c.L.Unlock()
156			runtime.Gosched()
157			c.L.Lock()
158		}
159		c.L.Unlock()
160		done <- true
161	}()
162	go func() {
163		c.L.Lock()
164		for {
165			if x == 2 {
166				c.Wait()
167				if x != 3 {
168					t.Error("want 3")
169				}
170				break
171			}
172			if x == 3 {
173				break
174			}
175			c.L.Unlock()
176			runtime.Gosched()
177			c.L.Lock()
178		}
179		c.L.Unlock()
180		done <- true
181	}()
182	<-done
183	<-done
184	<-done
185}
186
187func TestCondSignalStealing(t *testing.T) {
188	for iters := 0; iters < 1000; iters++ {
189		var m Mutex
190		cond := NewCond(&m)
191
192		// Start a waiter.
193		ch := make(chan struct{})
194		go func() {
195			m.Lock()
196			ch <- struct{}{}
197			cond.Wait()
198			m.Unlock()
199
200			ch <- struct{}{}
201		}()
202
203		<-ch
204		m.Lock()
205		m.Unlock()
206
207		// We know that the waiter is in the cond.Wait() call because we
208		// synchronized with it, then acquired/released the mutex it was
209		// holding when we synchronized.
210		//
211		// Start two goroutines that will race: one will broadcast on
212		// the cond var, the other will wait on it.
213		//
214		// The new waiter may or may not get notified, but the first one
215		// has to be notified.
216		done := false
217		go func() {
218			cond.Broadcast()
219		}()
220
221		go func() {
222			m.Lock()
223			for !done {
224				cond.Wait()
225			}
226			m.Unlock()
227		}()
228
229		// Check that the first waiter does get signaled.
230		<-ch
231
232		// Release the second waiter in case it didn't get the
233		// broadcast.
234		m.Lock()
235		done = true
236		m.Unlock()
237		cond.Broadcast()
238	}
239}
240
241func TestCondCopy(t *testing.T) {
242	defer func() {
243		err := recover()
244		if err == nil || err.(string) != "sync.Cond is copied" {
245			t.Fatalf("got %v, expect sync.Cond is copied", err)
246		}
247	}()
248	c := Cond{L: &Mutex{}}
249	c.Signal()
250	var c2 Cond
251	reflect.ValueOf(&c2).Elem().Set(reflect.ValueOf(&c).Elem()) // c2 := c, hidden from vet
252	c2.Signal()
253}
254
255func BenchmarkCond1(b *testing.B) {
256	benchmarkCond(b, 1)
257}
258
259func BenchmarkCond2(b *testing.B) {
260	benchmarkCond(b, 2)
261}
262
263func BenchmarkCond4(b *testing.B) {
264	benchmarkCond(b, 4)
265}
266
267func BenchmarkCond8(b *testing.B) {
268	benchmarkCond(b, 8)
269}
270
271func BenchmarkCond16(b *testing.B) {
272	benchmarkCond(b, 16)
273}
274
275func BenchmarkCond32(b *testing.B) {
276	benchmarkCond(b, 32)
277}
278
279func benchmarkCond(b *testing.B, waiters int) {
280	c := NewCond(&Mutex{})
281	done := make(chan bool)
282	id := 0
283
284	for routine := 0; routine < waiters+1; routine++ {
285		go func() {
286			for i := 0; i < b.N; i++ {
287				c.L.Lock()
288				if id == -1 {
289					c.L.Unlock()
290					break
291				}
292				id++
293				if id == waiters+1 {
294					id = 0
295					c.Broadcast()
296				} else {
297					c.Wait()
298				}
299				c.L.Unlock()
300			}
301			c.L.Lock()
302			id = -1
303			c.Broadcast()
304			c.L.Unlock()
305			done <- true
306		}()
307	}
308	for routine := 0; routine < waiters+1; routine++ {
309		<-done
310	}
311}
312