blob: 9019f8f102827ff57636acbee43a41f5209dce9a [file] [log] [blame]
Gustavo Niemeyer05b1dbd2011-02-16 14:11:07 -05001// 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.
4package sync_test
5
6import (
7 . "sync"
Dmitriy Vyukov5a20b4a2013-08-13 14:45:36 +04008
9 "runtime"
Gustavo Niemeyer05b1dbd2011-02-16 14:11:07 -050010 "testing"
Wedson Almeida Filho8e7072c2016-01-24 19:23:48 +010011 "time"
Gustavo Niemeyer05b1dbd2011-02-16 14:11:07 -050012)
13
14func TestCondSignal(t *testing.T) {
15 var m Mutex
16 c := NewCond(&m)
Russ Cox12b78752011-02-25 14:29:47 -050017 n := 2
Gustavo Niemeyer05b1dbd2011-02-16 14:11:07 -050018 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
Gustavo Niemeyer17bfa322011-06-01 20:30:42 -030052func 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
Gustavo Niemeyer05b1dbd2011-02-16 14:11:07 -050079func 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}
Dmitriy Vyukov5a20b4a2013-08-13 14:45:36 +0400130
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 {
Ian Lance Taylora1458902016-11-14 21:34:58 -0800140 t.Error("want 2")
Dmitriy Vyukov5a20b4a2013-08-13 14:45:36 +0400141 }
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 {
Ian Lance Taylora1458902016-11-14 21:34:58 -0800168 t.Error("want 3")
Dmitriy Vyukov5a20b4a2013-08-13 14:45:36 +0400169 }
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
Wedson Almeida Filho8e7072c2016-01-24 19:23:48 +0100187func 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 select {
231 case <-ch:
232 case <-time.After(2 * time.Second):
233 t.Fatalf("First waiter didn't get broadcast.")
234 }
235
236 // Release the second waiter in case it didn't get the
237 // broadcast.
238 m.Lock()
239 done = true
240 m.Unlock()
241 cond.Broadcast()
242 }
243}
244
Dmitriy Vyukov5a20b4a2013-08-13 14:45:36 +0400245func TestCondCopy(t *testing.T) {
246 defer func() {
247 err := recover()
248 if err == nil || err.(string) != "sync.Cond is copied" {
249 t.Fatalf("got %v, expect sync.Cond is copied", err)
250 }
251 }()
252 c := Cond{L: &Mutex{}}
253 c.Signal()
254 c2 := c
255 c2.Signal()
256}
257
258func BenchmarkCond1(b *testing.B) {
259 benchmarkCond(b, 1)
260}
261
262func BenchmarkCond2(b *testing.B) {
263 benchmarkCond(b, 2)
264}
265
266func BenchmarkCond4(b *testing.B) {
267 benchmarkCond(b, 4)
268}
269
270func BenchmarkCond8(b *testing.B) {
271 benchmarkCond(b, 8)
272}
273
274func BenchmarkCond16(b *testing.B) {
275 benchmarkCond(b, 16)
276}
277
278func BenchmarkCond32(b *testing.B) {
279 benchmarkCond(b, 32)
280}
281
282func benchmarkCond(b *testing.B, waiters int) {
283 c := NewCond(&Mutex{})
284 done := make(chan bool)
285 id := 0
286
287 for routine := 0; routine < waiters+1; routine++ {
288 go func() {
289 for i := 0; i < b.N; i++ {
290 c.L.Lock()
291 if id == -1 {
292 c.L.Unlock()
293 break
294 }
295 id++
296 if id == waiters+1 {
297 id = 0
298 c.Broadcast()
299 } else {
300 c.Wait()
301 }
302 c.L.Unlock()
303 }
304 c.L.Lock()
305 id = -1
306 c.Broadcast()
307 c.L.Unlock()
308 done <- true
309 }()
310 }
311 for routine := 0; routine < waiters+1; routine++ {
312 <-done
313 }
314}