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 package sync_test 5 6 import ( 7 . "sync" 8 9 "runtime" 10 "testing" 11 "time" 12 ) 13 14 func 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 52 func 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 79 func 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 131 func 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 187 func 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 245 func 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 258 func BenchmarkCond1(b *testing.B) { 259 benchmarkCond(b, 1) 260 } 261 262 func BenchmarkCond2(b *testing.B) { 263 benchmarkCond(b, 2) 264 } 265 266 func BenchmarkCond4(b *testing.B) { 267 benchmarkCond(b, 4) 268 } 269 270 func BenchmarkCond8(b *testing.B) { 271 benchmarkCond(b, 8) 272 } 273 274 func BenchmarkCond16(b *testing.B) { 275 benchmarkCond(b, 16) 276 } 277 278 func BenchmarkCond32(b *testing.B) { 279 benchmarkCond(b, 32) 280 } 281 282 func 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 } 315