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 ) 12 13 func TestCondSignal(t *testing.T) { 14 var m Mutex 15 c := NewCond(&m) 16 n := 2 17 running := make(chan bool, n) 18 awake := make(chan bool, n) 19 for i := 0; i < n; i++ { 20 go func() { 21 m.Lock() 22 running <- true 23 c.Wait() 24 awake <- true 25 m.Unlock() 26 }() 27 } 28 for i := 0; i < n; i++ { 29 <-running // Wait for everyone to run. 30 } 31 for n > 0 { 32 select { 33 case <-awake: 34 t.Fatal("goroutine not asleep") 35 default: 36 } 37 m.Lock() 38 c.Signal() 39 m.Unlock() 40 <-awake // Will deadlock if no goroutine wakes up 41 select { 42 case <-awake: 43 t.Fatal("too many goroutines awake") 44 default: 45 } 46 n-- 47 } 48 c.Signal() 49 } 50 51 func TestCondSignalGenerations(t *testing.T) { 52 var m Mutex 53 c := NewCond(&m) 54 n := 100 55 running := make(chan bool, n) 56 awake := make(chan int, n) 57 for i := 0; i < n; i++ { 58 go func(i int) { 59 m.Lock() 60 running <- true 61 c.Wait() 62 awake <- i 63 m.Unlock() 64 }(i) 65 if i > 0 { 66 a := <-awake 67 if a != i-1 { 68 t.Fatalf("wrong goroutine woke up: want %d, got %d", i-1, a) 69 } 70 } 71 <-running 72 m.Lock() 73 c.Signal() 74 m.Unlock() 75 } 76 } 77 78 func TestCondBroadcast(t *testing.T) { 79 var m Mutex 80 c := NewCond(&m) 81 n := 200 82 running := make(chan int, n) 83 awake := make(chan int, n) 84 exit := false 85 for i := 0; i < n; i++ { 86 go func(g int) { 87 m.Lock() 88 for !exit { 89 running <- g 90 c.Wait() 91 awake <- g 92 } 93 m.Unlock() 94 }(i) 95 } 96 for i := 0; i < n; i++ { 97 for i := 0; i < n; i++ { 98 <-running // Will deadlock unless n are running. 99 } 100 if i == n-1 { 101 m.Lock() 102 exit = true 103 m.Unlock() 104 } 105 select { 106 case <-awake: 107 t.Fatal("goroutine not asleep") 108 default: 109 } 110 m.Lock() 111 c.Broadcast() 112 m.Unlock() 113 seen := make([]bool, n) 114 for i := 0; i < n; i++ { 115 g := <-awake 116 if seen[g] { 117 t.Fatal("goroutine woke up twice") 118 } 119 seen[g] = true 120 } 121 } 122 select { 123 case <-running: 124 t.Fatal("goroutine did not exit") 125 default: 126 } 127 c.Broadcast() 128 } 129 130 func TestRace(t *testing.T) { 131 x := 0 132 c := NewCond(&Mutex{}) 133 done := make(chan bool) 134 go func() { 135 c.L.Lock() 136 x = 1 137 c.Wait() 138 if x != 2 { 139 t.Fatal("want 2") 140 } 141 x = 3 142 c.Signal() 143 c.L.Unlock() 144 done <- true 145 }() 146 go func() { 147 c.L.Lock() 148 for { 149 if x == 1 { 150 x = 2 151 c.Signal() 152 break 153 } 154 c.L.Unlock() 155 runtime.Gosched() 156 c.L.Lock() 157 } 158 c.L.Unlock() 159 done <- true 160 }() 161 go func() { 162 c.L.Lock() 163 for { 164 if x == 2 { 165 c.Wait() 166 if x != 3 { 167 t.Fatal("want 3") 168 } 169 break 170 } 171 if x == 3 { 172 break 173 } 174 c.L.Unlock() 175 runtime.Gosched() 176 c.L.Lock() 177 } 178 c.L.Unlock() 179 done <- true 180 }() 181 <-done 182 <-done 183 <-done 184 } 185 186 func TestCondCopy(t *testing.T) { 187 defer func() { 188 err := recover() 189 if err == nil || err.(string) != "sync.Cond is copied" { 190 t.Fatalf("got %v, expect sync.Cond is copied", err) 191 } 192 }() 193 c := Cond{L: &Mutex{}} 194 c.Signal() 195 c2 := c 196 c2.Signal() 197 } 198 199 func BenchmarkCond1(b *testing.B) { 200 benchmarkCond(b, 1) 201 } 202 203 func BenchmarkCond2(b *testing.B) { 204 benchmarkCond(b, 2) 205 } 206 207 func BenchmarkCond4(b *testing.B) { 208 benchmarkCond(b, 4) 209 } 210 211 func BenchmarkCond8(b *testing.B) { 212 benchmarkCond(b, 8) 213 } 214 215 func BenchmarkCond16(b *testing.B) { 216 benchmarkCond(b, 16) 217 } 218 219 func BenchmarkCond32(b *testing.B) { 220 benchmarkCond(b, 32) 221 } 222 223 func benchmarkCond(b *testing.B, waiters int) { 224 c := NewCond(&Mutex{}) 225 done := make(chan bool) 226 id := 0 227 228 for routine := 0; routine < waiters+1; routine++ { 229 go func() { 230 for i := 0; i < b.N; i++ { 231 c.L.Lock() 232 if id == -1 { 233 c.L.Unlock() 234 break 235 } 236 id++ 237 if id == waiters+1 { 238 id = 0 239 c.Broadcast() 240 } else { 241 c.Wait() 242 } 243 c.L.Unlock() 244 } 245 c.L.Lock() 246 id = -1 247 c.Broadcast() 248 c.L.Unlock() 249 done <- true 250 }() 251 } 252 for routine := 0; routine < waiters+1; routine++ { 253 <-done 254 } 255 } 256