github.com/sandwich-go/boost@v1.3.29/xsync/cond_test.go (about) 1 package xsync 2 3 import ( 4 "fmt" 5 "runtime" 6 "sync" 7 "testing" 8 ) 9 10 func TestCondSignal(t *testing.T) { 11 var m sync.Mutex 12 c := NewCond(&m) 13 n := 2 14 running := make(chan bool, n) 15 awake := make(chan bool, n) 16 for i := 0; i < n; i++ { 17 go func() { 18 m.Lock() 19 running <- true 20 c.Wait() 21 awake <- true 22 m.Unlock() 23 }() 24 } 25 for i := 0; i < n; i++ { 26 <-running // Wait for everyone to run. 27 } 28 for n > 0 { 29 select { 30 case <-awake: 31 t.Fatal("goroutine not asleep") 32 default: 33 } 34 m.Lock() 35 c.Signal() 36 m.Unlock() 37 <-awake // Will deadlock if no goroutine wakes up 38 select { 39 case <-awake: 40 t.Fatal("too many goroutines awake") 41 default: 42 } 43 n-- 44 } 45 c.Signal() 46 } 47 48 func TestCondSignalGenerations(t *testing.T) { 49 var m sync.Mutex 50 c := NewCond(&m) 51 n := 100 52 running := make(chan bool, n) 53 awake := make(chan int, n) 54 for i := 0; i < n; i++ { 55 go func(i int) { 56 m.Lock() 57 running <- true 58 c.Wait() 59 awake <- i 60 m.Unlock() 61 }(i) 62 if i > 0 { 63 a := <-awake 64 if a != i-1 { 65 t.Fatalf("wrong goroutine woke up: want %d, got %d", i-1, a) 66 } 67 } 68 <-running 69 m.Lock() 70 c.Signal() 71 m.Unlock() 72 } 73 } 74 75 func TestCondBroadcast(t *testing.T) { 76 var m sync.Mutex 77 c := NewCond(&m) 78 n := 200 79 running := make(chan int, n) 80 awake := make(chan int, n) 81 exit := false 82 for i := 0; i < n; i++ { 83 go func(g int) { 84 m.Lock() 85 for !exit { 86 running <- g 87 c.Wait() 88 awake <- g 89 } 90 m.Unlock() 91 }(i) 92 } 93 for i := 0; i < n; i++ { 94 for i := 0; i < n; i++ { 95 <-running // Will deadlock unless n are running. 96 } 97 if i == n-1 { 98 m.Lock() 99 exit = true 100 m.Unlock() 101 } 102 select { 103 case <-awake: 104 t.Fatal("goroutine not asleep") 105 default: 106 } 107 m.Lock() 108 c.Broadcast() 109 m.Unlock() 110 seen := make([]bool, n) 111 for i := 0; i < n; i++ { 112 g := <-awake 113 if seen[g] { 114 t.Fatal("goroutine woke up twice") 115 } 116 seen[g] = true 117 } 118 } 119 select { 120 case <-running: 121 t.Fatal("goroutine did not exit") 122 default: 123 } 124 c.Broadcast() 125 } 126 127 func TestRace(t *testing.T) { 128 x := 0 129 c := NewCond(&sync.Mutex{}) 130 done := make(chan bool) 131 go func() { 132 c.L.Lock() 133 x = 1 134 c.Wait() 135 if x != 2 { 136 t.Fatal("want 2") 137 } 138 x = 3 139 c.Signal() 140 c.L.Unlock() 141 done <- true 142 }() 143 go func() { 144 c.L.Lock() 145 for { 146 if x == 1 { 147 x = 2 148 c.Signal() 149 break 150 } 151 c.L.Unlock() 152 runtime.Gosched() 153 c.L.Lock() 154 } 155 c.L.Unlock() 156 done <- true 157 }() 158 go func() { 159 c.L.Lock() 160 for { 161 if x == 2 { 162 c.Wait() 163 if x != 3 { 164 t.Fatal("want 3") 165 } 166 break 167 } 168 if x == 3 { 169 break 170 } 171 c.L.Unlock() 172 runtime.Gosched() 173 c.L.Lock() 174 } 175 c.L.Unlock() 176 done <- true 177 }() 178 <-done 179 <-done 180 <-done 181 } 182 183 // Bench: Rename this function to TestBench for running benchmarks 184 func Bench(t *testing.T) { 185 waitvals := []int{1, 2, 4, 8} 186 maxprocs := []int{1, 2, 4} 187 fmt.Printf("procs\twaiters\told\tnew\tdelta\n") 188 for _, procs := range maxprocs { 189 runtime.GOMAXPROCS(procs) 190 for _, waiters := range waitvals { 191 oldbench := func(b *testing.B) { 192 benchmarkCond(b, waiters) 193 } 194 oldbr := testing.Benchmark(oldbench) 195 newbench := func(b *testing.B) { 196 benchmarkCond2(b, waiters) 197 } 198 newbr := testing.Benchmark(newbench) 199 oldns := oldbr.NsPerOp() 200 newns := newbr.NsPerOp() 201 percent := float64(newns-oldns) * 100.0 / float64(oldns) 202 fmt.Printf("%d\t%d\t%d\t%d\t%6.2f%%\n", procs, waiters, oldns, newns, percent) 203 } 204 } 205 } 206 207 func benchmarkCond2(b *testing.B, waiters int) { 208 c := NewCond(&sync.Mutex{}) 209 done := make(chan bool) 210 id := 0 211 212 for routine := 0; routine < waiters+1; routine++ { 213 go func() { 214 for i := 0; i < b.N; i++ { 215 c.L.Lock() 216 if id == -1 { 217 c.L.Unlock() 218 break 219 } 220 id++ 221 if id == waiters+1 { 222 id = 0 223 c.Broadcast() 224 } else { 225 c.Wait() 226 } 227 c.L.Unlock() 228 } 229 c.L.Lock() 230 id = -1 231 c.Broadcast() 232 c.L.Unlock() 233 done <- true 234 }() 235 } 236 for routine := 0; routine < waiters+1; routine++ { 237 <-done 238 } 239 } 240 241 func benchmarkCond(b *testing.B, waiters int) { 242 c := sync.NewCond(&sync.Mutex{}) 243 done := make(chan bool) 244 id := 0 245 246 for routine := 0; routine < waiters+1; routine++ { 247 go func() { 248 for i := 0; i < b.N; i++ { 249 c.L.Lock() 250 if id == -1 { 251 c.L.Unlock() 252 break 253 } 254 id++ 255 if id == waiters+1 { 256 id = 0 257 c.Broadcast() 258 } else { 259 c.Wait() 260 } 261 c.L.Unlock() 262 } 263 c.L.Lock() 264 id = -1 265 c.Broadcast() 266 c.L.Unlock() 267 done <- true 268 }() 269 } 270 for routine := 0; routine < waiters+1; routine++ { 271 <-done 272 } 273 }