Source file
src/sync/mutex_test.go
1
2
3
4
5
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 "time"
19 )
20
21 func HammerSemaphore(s *uint32, loops int, cdone chan bool) {
22 for i := 0; i < loops; i++ {
23 Runtime_Semacquire(s)
24 Runtime_Semrelease(s, false, 0)
25 }
26 cdone <- true
27 }
28
29 func TestSemaphore(t *testing.T) {
30 s := new(uint32)
31 *s = 1
32 c := make(chan bool)
33 for i := 0; i < 10; i++ {
34 go HammerSemaphore(s, 1000, c)
35 }
36 for i := 0; i < 10; i++ {
37 <-c
38 }
39 }
40
41 func BenchmarkUncontendedSemaphore(b *testing.B) {
42 s := new(uint32)
43 *s = 1
44 HammerSemaphore(s, b.N, make(chan bool, 2))
45 }
46
47 func BenchmarkContendedSemaphore(b *testing.B) {
48 b.StopTimer()
49 s := new(uint32)
50 *s = 1
51 c := make(chan bool)
52 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
53 b.StartTimer()
54
55 go HammerSemaphore(s, b.N/2, c)
56 go HammerSemaphore(s, b.N/2, c)
57 <-c
58 <-c
59 }
60
61 func HammerMutex(m *Mutex, loops int, cdone chan bool) {
62 for i := 0; i < loops; i++ {
63 if i%3 == 0 {
64 if m.TryLock() {
65 m.Unlock()
66 }
67 continue
68 }
69 m.Lock()
70 m.Unlock()
71 }
72 cdone <- true
73 }
74
75 func TestMutex(t *testing.T) {
76 if n := runtime.SetMutexProfileFraction(1); n != 0 {
77 t.Logf("got mutexrate %d expected 0", n)
78 }
79 defer runtime.SetMutexProfileFraction(0)
80
81 m := new(Mutex)
82
83 m.Lock()
84 if m.TryLock() {
85 t.Fatalf("TryLock succeeded with mutex locked")
86 }
87 m.Unlock()
88 if !m.TryLock() {
89 t.Fatalf("TryLock failed with mutex unlocked")
90 }
91 m.Unlock()
92
93 c := make(chan bool)
94 for i := 0; i < 10; i++ {
95 go HammerMutex(m, 1000, c)
96 }
97 for i := 0; i < 10; i++ {
98 <-c
99 }
100 }
101
102 var misuseTests = []struct {
103 name string
104 f func()
105 }{
106 {
107 "Mutex.Unlock",
108 func() {
109 var mu Mutex
110 mu.Unlock()
111 },
112 },
113 {
114 "Mutex.Unlock2",
115 func() {
116 var mu Mutex
117 mu.Lock()
118 mu.Unlock()
119 mu.Unlock()
120 },
121 },
122 {
123 "RWMutex.Unlock",
124 func() {
125 var mu RWMutex
126 mu.Unlock()
127 },
128 },
129 {
130 "RWMutex.Unlock2",
131 func() {
132 var mu RWMutex
133 mu.RLock()
134 mu.Unlock()
135 },
136 },
137 {
138 "RWMutex.Unlock3",
139 func() {
140 var mu RWMutex
141 mu.Lock()
142 mu.Unlock()
143 mu.Unlock()
144 },
145 },
146 {
147 "RWMutex.RUnlock",
148 func() {
149 var mu RWMutex
150 mu.RUnlock()
151 },
152 },
153 {
154 "RWMutex.RUnlock2",
155 func() {
156 var mu RWMutex
157 mu.Lock()
158 mu.RUnlock()
159 },
160 },
161 {
162 "RWMutex.RUnlock3",
163 func() {
164 var mu RWMutex
165 mu.RLock()
166 mu.RUnlock()
167 mu.RUnlock()
168 },
169 },
170 }
171
172 func init() {
173 if len(os.Args) == 3 && os.Args[1] == "TESTMISUSE" {
174 for _, test := range misuseTests {
175 if test.name == os.Args[2] {
176 func() {
177 defer func() { recover() }()
178 test.f()
179 }()
180 fmt.Printf("test completed\n")
181 os.Exit(0)
182 }
183 }
184 fmt.Printf("unknown test\n")
185 os.Exit(0)
186 }
187 }
188
189 func TestMutexMisuse(t *testing.T) {
190 testenv.MustHaveExec(t)
191 for _, test := range misuseTests {
192 out, err := exec.Command(os.Args[0], "TESTMISUSE", test.name).CombinedOutput()
193 if err == nil || !strings.Contains(string(out), "unlocked") {
194 t.Errorf("%s: did not find failure with message about unlocked lock: %s\n%s\n", test.name, err, out)
195 }
196 }
197 }
198
199 func TestMutexFairness(t *testing.T) {
200 var mu Mutex
201 stop := make(chan bool)
202 defer close(stop)
203 go func() {
204 for {
205 mu.Lock()
206 time.Sleep(100 * time.Microsecond)
207 mu.Unlock()
208 select {
209 case <-stop:
210 return
211 default:
212 }
213 }
214 }()
215 done := make(chan bool, 1)
216 go func() {
217 for i := 0; i < 10; i++ {
218 time.Sleep(100 * time.Microsecond)
219 mu.Lock()
220 mu.Unlock()
221 }
222 done <- true
223 }()
224 select {
225 case <-done:
226 case <-time.After(10 * time.Second):
227 t.Fatalf("can't acquire Mutex in 10 seconds")
228 }
229 }
230
231 func BenchmarkMutexUncontended(b *testing.B) {
232 type PaddedMutex struct {
233 Mutex
234 pad [128]uint8
235 }
236 b.RunParallel(func(pb *testing.PB) {
237 var mu PaddedMutex
238 for pb.Next() {
239 mu.Lock()
240 mu.Unlock()
241 }
242 })
243 }
244
245 func benchmarkMutex(b *testing.B, slack, work bool) {
246 var mu Mutex
247 if slack {
248 b.SetParallelism(10)
249 }
250 b.RunParallel(func(pb *testing.PB) {
251 foo := 0
252 for pb.Next() {
253 mu.Lock()
254 mu.Unlock()
255 if work {
256 for i := 0; i < 100; i++ {
257 foo *= 2
258 foo /= 2
259 }
260 }
261 }
262 _ = foo
263 })
264 }
265
266 func BenchmarkMutex(b *testing.B) {
267 benchmarkMutex(b, false, false)
268 }
269
270 func BenchmarkMutexSlack(b *testing.B) {
271 benchmarkMutex(b, true, false)
272 }
273
274 func BenchmarkMutexWork(b *testing.B) {
275 benchmarkMutex(b, false, true)
276 }
277
278 func BenchmarkMutexWorkSlack(b *testing.B) {
279 benchmarkMutex(b, true, true)
280 }
281
282 func BenchmarkMutexNoSpin(b *testing.B) {
283
284
285
286
287
288
289 var m Mutex
290 var acc0, acc1 uint64
291 b.SetParallelism(4)
292 b.RunParallel(func(pb *testing.PB) {
293 c := make(chan bool)
294 var data [4 << 10]uint64
295 for i := 0; pb.Next(); i++ {
296 if i%4 == 0 {
297 m.Lock()
298 acc0 -= 100
299 acc1 += 100
300 m.Unlock()
301 } else {
302 for i := 0; i < len(data); i += 4 {
303 data[i]++
304 }
305
306
307 go func() {
308 c <- true
309 }()
310 <-c
311 }
312 }
313 })
314 }
315
316 func BenchmarkMutexSpin(b *testing.B) {
317
318
319
320
321 var m Mutex
322 var acc0, acc1 uint64
323 b.RunParallel(func(pb *testing.PB) {
324 var data [16 << 10]uint64
325 for i := 0; pb.Next(); i++ {
326 m.Lock()
327 acc0 -= 100
328 acc1 += 100
329 m.Unlock()
330 for i := 0; i < len(data); i += 4 {
331 data[i]++
332 }
333 }
334 })
335 }
336
View as plain text