github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/sync/cond_test.go (about) 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 5 package sync_test 6 7 import ( 8 "reflect" 9 "runtime" 10 . "sync" 11 "testing" 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 <-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 241 func 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 255 func BenchmarkCond1(b *testing.B) { 256 benchmarkCond(b, 1) 257 } 258 259 func BenchmarkCond2(b *testing.B) { 260 benchmarkCond(b, 2) 261 } 262 263 func BenchmarkCond4(b *testing.B) { 264 benchmarkCond(b, 4) 265 } 266 267 func BenchmarkCond8(b *testing.B) { 268 benchmarkCond(b, 8) 269 } 270 271 func BenchmarkCond16(b *testing.B) { 272 benchmarkCond(b, 16) 273 } 274 275 func BenchmarkCond32(b *testing.B) { 276 benchmarkCond(b, 32) 277 } 278 279 func 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 }