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