runtime: omit nil-channel cases from selectgo's orders
[gcc.git] / libgo / go / sync / mutex_test.go
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 "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 m.Lock()
64 m.Unlock()
65 }
66 cdone <- true
67 }
68
69 func TestMutex(t *testing.T) {
70 if n := runtime.SetMutexProfileFraction(1); n != 0 {
71 t.Logf("got mutexrate %d expected 0", n)
72 }
73 defer runtime.SetMutexProfileFraction(0)
74 m := new(Mutex)
75 c := make(chan bool)
76 for i := 0; i < 10; i++ {
77 go HammerMutex(m, 1000, c)
78 }
79 for i := 0; i < 10; i++ {
80 <-c
81 }
82 }
83
84 var misuseTests = []struct {
85 name string
86 f func()
87 }{
88 {
89 "Mutex.Unlock",
90 func() {
91 var mu Mutex
92 mu.Unlock()
93 },
94 },
95 {
96 "Mutex.Unlock2",
97 func() {
98 var mu Mutex
99 mu.Lock()
100 mu.Unlock()
101 mu.Unlock()
102 },
103 },
104 {
105 "RWMutex.Unlock",
106 func() {
107 var mu RWMutex
108 mu.Unlock()
109 },
110 },
111 {
112 "RWMutex.Unlock2",
113 func() {
114 var mu RWMutex
115 mu.RLock()
116 mu.Unlock()
117 },
118 },
119 {
120 "RWMutex.Unlock3",
121 func() {
122 var mu RWMutex
123 mu.Lock()
124 mu.Unlock()
125 mu.Unlock()
126 },
127 },
128 {
129 "RWMutex.RUnlock",
130 func() {
131 var mu RWMutex
132 mu.RUnlock()
133 },
134 },
135 {
136 "RWMutex.RUnlock2",
137 func() {
138 var mu RWMutex
139 mu.Lock()
140 mu.RUnlock()
141 },
142 },
143 {
144 "RWMutex.RUnlock3",
145 func() {
146 var mu RWMutex
147 mu.RLock()
148 mu.RUnlock()
149 mu.RUnlock()
150 },
151 },
152 }
153
154 func init() {
155 if len(os.Args) == 3 && os.Args[1] == "TESTMISUSE" {
156 for _, test := range misuseTests {
157 if test.name == os.Args[2] {
158 func() {
159 defer func() { recover() }()
160 test.f()
161 }()
162 fmt.Printf("test completed\n")
163 os.Exit(0)
164 }
165 }
166 fmt.Printf("unknown test\n")
167 os.Exit(0)
168 }
169 }
170
171 func TestMutexMisuse(t *testing.T) {
172 testenv.MustHaveExec(t)
173 for _, test := range misuseTests {
174 out, err := exec.Command(os.Args[0], "TESTMISUSE", test.name).CombinedOutput()
175 if err == nil || !strings.Contains(string(out), "unlocked") {
176 t.Errorf("%s: did not find failure with message about unlocked lock: %s\n%s\n", test.name, err, out)
177 }
178 }
179 }
180
181 func TestMutexFairness(t *testing.T) {
182 var mu Mutex
183 stop := make(chan bool)
184 defer close(stop)
185 go func() {
186 for {
187 mu.Lock()
188 time.Sleep(100 * time.Microsecond)
189 mu.Unlock()
190 select {
191 case <-stop:
192 return
193 default:
194 }
195 }
196 }()
197 done := make(chan bool)
198 go func() {
199 for i := 0; i < 10; i++ {
200 time.Sleep(100 * time.Microsecond)
201 mu.Lock()
202 mu.Unlock()
203 }
204 done <- true
205 }()
206 select {
207 case <-done:
208 case <-time.After(10 * time.Second):
209 t.Fatalf("can't acquire Mutex in 10 seconds")
210 }
211 }
212
213 func BenchmarkMutexUncontended(b *testing.B) {
214 type PaddedMutex struct {
215 Mutex
216 pad [128]uint8
217 }
218 b.RunParallel(func(pb *testing.PB) {
219 var mu PaddedMutex
220 for pb.Next() {
221 mu.Lock()
222 mu.Unlock()
223 }
224 })
225 }
226
227 func benchmarkMutex(b *testing.B, slack, work bool) {
228 var mu Mutex
229 if slack {
230 b.SetParallelism(10)
231 }
232 b.RunParallel(func(pb *testing.PB) {
233 foo := 0
234 for pb.Next() {
235 mu.Lock()
236 mu.Unlock()
237 if work {
238 for i := 0; i < 100; i++ {
239 foo *= 2
240 foo /= 2
241 }
242 }
243 }
244 _ = foo
245 })
246 }
247
248 func BenchmarkMutex(b *testing.B) {
249 benchmarkMutex(b, false, false)
250 }
251
252 func BenchmarkMutexSlack(b *testing.B) {
253 benchmarkMutex(b, true, false)
254 }
255
256 func BenchmarkMutexWork(b *testing.B) {
257 benchmarkMutex(b, false, true)
258 }
259
260 func BenchmarkMutexWorkSlack(b *testing.B) {
261 benchmarkMutex(b, true, true)
262 }
263
264 func BenchmarkMutexNoSpin(b *testing.B) {
265 // This benchmark models a situation where spinning in the mutex should be
266 // non-profitable and allows to confirm that spinning does not do harm.
267 // To achieve this we create excess of goroutines most of which do local work.
268 // These goroutines yield during local work, so that switching from
269 // a blocked goroutine to other goroutines is profitable.
270 // As a matter of fact, this benchmark still triggers some spinning in the mutex.
271 var m Mutex
272 var acc0, acc1 uint64
273 b.SetParallelism(4)
274 b.RunParallel(func(pb *testing.PB) {
275 c := make(chan bool)
276 var data [4 << 10]uint64
277 for i := 0; pb.Next(); i++ {
278 if i%4 == 0 {
279 m.Lock()
280 acc0 -= 100
281 acc1 += 100
282 m.Unlock()
283 } else {
284 for i := 0; i < len(data); i += 4 {
285 data[i]++
286 }
287 // Elaborate way to say runtime.Gosched
288 // that does not put the goroutine onto global runq.
289 go func() {
290 c <- true
291 }()
292 <-c
293 }
294 }
295 })
296 }
297
298 func BenchmarkMutexSpin(b *testing.B) {
299 // This benchmark models a situation where spinning in the mutex should be
300 // profitable. To achieve this we create a goroutine per-proc.
301 // These goroutines access considerable amount of local data so that
302 // unnecessary rescheduling is penalized by cache misses.
303 var m Mutex
304 var acc0, acc1 uint64
305 b.RunParallel(func(pb *testing.PB) {
306 var data [16 << 10]uint64
307 for i := 0; pb.Next(); i++ {
308 m.Lock()
309 acc0 -= 100
310 acc1 += 100
311 m.Unlock()
312 for i := 0; i < len(data); i += 4 {
313 data[i]++
314 }
315 }
316 })
317 }