github.com/fenixara/go@v0.0.0-20170127160404-96ea0918e670/src/sync/mutex_test.go (about) 1 // Copyright 2009 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 // GOMAXPROCS=10 go test 6 7 package sync_test 8 9 import ( 10 "fmt" 11 "internal/testenv" 12 "os" 13 "os/exec" 14 "runtime" 15 "strings" 16 . "sync" 17 "testing" 18 ) 19 20 func HammerSemaphore(s *uint32, loops int, cdone chan bool) { 21 for i := 0; i < loops; i++ { 22 Runtime_Semacquire(s) 23 Runtime_Semrelease(s) 24 } 25 cdone <- true 26 } 27 28 func TestSemaphore(t *testing.T) { 29 s := new(uint32) 30 *s = 1 31 c := make(chan bool) 32 for i := 0; i < 10; i++ { 33 go HammerSemaphore(s, 1000, c) 34 } 35 for i := 0; i < 10; i++ { 36 <-c 37 } 38 } 39 40 func BenchmarkUncontendedSemaphore(b *testing.B) { 41 s := new(uint32) 42 *s = 1 43 HammerSemaphore(s, b.N, make(chan bool, 2)) 44 } 45 46 func BenchmarkContendedSemaphore(b *testing.B) { 47 b.StopTimer() 48 s := new(uint32) 49 *s = 1 50 c := make(chan bool) 51 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) 52 b.StartTimer() 53 54 go HammerSemaphore(s, b.N/2, c) 55 go HammerSemaphore(s, b.N/2, c) 56 <-c 57 <-c 58 } 59 60 func HammerMutex(m *Mutex, loops int, cdone chan bool) { 61 for i := 0; i < loops; i++ { 62 m.Lock() 63 m.Unlock() 64 } 65 cdone <- true 66 } 67 68 func TestMutex(t *testing.T) { 69 if n := runtime.SetMutexProfileFraction(1); n != 0 { 70 t.Logf("got mutexrate %d expected 0", n) 71 } 72 defer runtime.SetMutexProfileFraction(0) 73 m := new(Mutex) 74 c := make(chan bool) 75 for i := 0; i < 10; i++ { 76 go HammerMutex(m, 1000, c) 77 } 78 for i := 0; i < 10; i++ { 79 <-c 80 } 81 } 82 83 var misuseTests = []struct { 84 name string 85 f func() 86 }{ 87 { 88 "Mutex.Unlock", 89 func() { 90 var mu Mutex 91 mu.Unlock() 92 }, 93 }, 94 { 95 "Mutex.Unlock2", 96 func() { 97 var mu Mutex 98 mu.Lock() 99 mu.Unlock() 100 mu.Unlock() 101 }, 102 }, 103 { 104 "RWMutex.Unlock", 105 func() { 106 var mu RWMutex 107 mu.Unlock() 108 }, 109 }, 110 { 111 "RWMutex.Unlock2", 112 func() { 113 var mu RWMutex 114 mu.RLock() 115 mu.Unlock() 116 }, 117 }, 118 { 119 "RWMutex.Unlock3", 120 func() { 121 var mu RWMutex 122 mu.Lock() 123 mu.Unlock() 124 mu.Unlock() 125 }, 126 }, 127 { 128 "RWMutex.RUnlock", 129 func() { 130 var mu RWMutex 131 mu.RUnlock() 132 }, 133 }, 134 { 135 "RWMutex.RUnlock2", 136 func() { 137 var mu RWMutex 138 mu.Lock() 139 mu.RUnlock() 140 }, 141 }, 142 { 143 "RWMutex.RUnlock3", 144 func() { 145 var mu RWMutex 146 mu.RLock() 147 mu.RUnlock() 148 mu.RUnlock() 149 }, 150 }, 151 } 152 153 func init() { 154 if len(os.Args) == 3 && os.Args[1] == "TESTMISUSE" { 155 for _, test := range misuseTests { 156 if test.name == os.Args[2] { 157 test.f() 158 fmt.Printf("test completed\n") 159 os.Exit(0) 160 } 161 } 162 fmt.Printf("unknown test\n") 163 os.Exit(0) 164 } 165 } 166 167 func TestMutexMisuse(t *testing.T) { 168 testenv.MustHaveExec(t) 169 for _, test := range misuseTests { 170 out, err := exec.Command(os.Args[0], "TESTMISUSE", test.name).CombinedOutput() 171 if err == nil || !strings.Contains(string(out), "unlocked") { 172 t.Errorf("%s: did not find failure with message about unlocked lock: %s\n%s\n", test.name, err, out) 173 } 174 } 175 } 176 177 func BenchmarkMutexUncontended(b *testing.B) { 178 type PaddedMutex struct { 179 Mutex 180 pad [128]uint8 181 } 182 b.RunParallel(func(pb *testing.PB) { 183 var mu PaddedMutex 184 for pb.Next() { 185 mu.Lock() 186 mu.Unlock() 187 } 188 }) 189 } 190 191 func benchmarkMutex(b *testing.B, slack, work bool) { 192 var mu Mutex 193 if slack { 194 b.SetParallelism(10) 195 } 196 b.RunParallel(func(pb *testing.PB) { 197 foo := 0 198 for pb.Next() { 199 mu.Lock() 200 mu.Unlock() 201 if work { 202 for i := 0; i < 100; i++ { 203 foo *= 2 204 foo /= 2 205 } 206 } 207 } 208 _ = foo 209 }) 210 } 211 212 func BenchmarkMutex(b *testing.B) { 213 benchmarkMutex(b, false, false) 214 } 215 216 func BenchmarkMutexSlack(b *testing.B) { 217 benchmarkMutex(b, true, false) 218 } 219 220 func BenchmarkMutexWork(b *testing.B) { 221 benchmarkMutex(b, false, true) 222 } 223 224 func BenchmarkMutexWorkSlack(b *testing.B) { 225 benchmarkMutex(b, true, true) 226 } 227 228 func BenchmarkMutexNoSpin(b *testing.B) { 229 // This benchmark models a situation where spinning in the mutex should be 230 // non-profitable and allows to confirm that spinning does not do harm. 231 // To achieve this we create excess of goroutines most of which do local work. 232 // These goroutines yield during local work, so that switching from 233 // a blocked goroutine to other goroutines is profitable. 234 // As a matter of fact, this benchmark still triggers some spinning in the mutex. 235 var m Mutex 236 var acc0, acc1 uint64 237 b.SetParallelism(4) 238 b.RunParallel(func(pb *testing.PB) { 239 c := make(chan bool) 240 var data [4 << 10]uint64 241 for i := 0; pb.Next(); i++ { 242 if i%4 == 0 { 243 m.Lock() 244 acc0 -= 100 245 acc1 += 100 246 m.Unlock() 247 } else { 248 for i := 0; i < len(data); i += 4 { 249 data[i]++ 250 } 251 // Elaborate way to say runtime.Gosched 252 // that does not put the goroutine onto global runq. 253 go func() { 254 c <- true 255 }() 256 <-c 257 } 258 } 259 }) 260 } 261 262 func BenchmarkMutexSpin(b *testing.B) { 263 // This benchmark models a situation where spinning in the mutex should be 264 // profitable. To achieve this we create a goroutine per-proc. 265 // These goroutines access considerable amount of local data so that 266 // unnecessary rescheduling is penalized by cache misses. 267 var m Mutex 268 var acc0, acc1 uint64 269 b.RunParallel(func(pb *testing.PB) { 270 var data [16 << 10]uint64 271 for i := 0; pb.Next(); i++ { 272 m.Lock() 273 acc0 -= 100 274 acc1 += 100 275 m.Unlock() 276 for i := 0; i < len(data); i += 4 { 277 data[i]++ 278 } 279 } 280 }) 281 }