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
7package sync_test
8
9import (
10	"runtime"
11	. "sync"
12	"sync/atomic"
13	"testing"
14)
15
16func HammerSemaphore(s *uint32, loops int, cdone chan bool) {
17	for i := 0; i < loops; i++ {
18		Runtime_Semacquire(s)
19		Runtime_Semrelease(s)
20	}
21	cdone <- true
22}
23
24func TestSemaphore(t *testing.T) {
25	s := new(uint32)
26	*s = 1
27	c := make(chan bool)
28	for i := 0; i < 10; i++ {
29		go HammerSemaphore(s, 1000, c)
30	}
31	for i := 0; i < 10; i++ {
32		<-c
33	}
34}
35
36func BenchmarkUncontendedSemaphore(b *testing.B) {
37	s := new(uint32)
38	*s = 1
39	HammerSemaphore(s, b.N, make(chan bool, 2))
40}
41
42func BenchmarkContendedSemaphore(b *testing.B) {
43	b.StopTimer()
44	s := new(uint32)
45	*s = 1
46	c := make(chan bool)
47	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
48	b.StartTimer()
49
50	go HammerSemaphore(s, b.N/2, c)
51	go HammerSemaphore(s, b.N/2, c)
52	<-c
53	<-c
54}
55
56func HammerMutex(m *Mutex, loops int, cdone chan bool) {
57	for i := 0; i < loops; i++ {
58		m.Lock()
59		m.Unlock()
60	}
61	cdone <- true
62}
63
64func TestMutex(t *testing.T) {
65	m := new(Mutex)
66	c := make(chan bool)
67	for i := 0; i < 10; i++ {
68		go HammerMutex(m, 1000, c)
69	}
70	for i := 0; i < 10; i++ {
71		<-c
72	}
73}
74
75func TestMutexPanic(t *testing.T) {
76	defer func() {
77		if recover() == nil {
78			t.Fatalf("unlock of unlocked mutex did not panic")
79		}
80	}()
81
82	var mu Mutex
83	mu.Lock()
84	mu.Unlock()
85	mu.Unlock()
86}
87
88func BenchmarkMutexUncontended(b *testing.B) {
89	type PaddedMutex struct {
90		Mutex
91		pad [128]uint8
92	}
93	const CallsPerSched = 1000
94	procs := runtime.GOMAXPROCS(-1)
95	N := int32(b.N / CallsPerSched)
96	c := make(chan bool, procs)
97	for p := 0; p < procs; p++ {
98		go func() {
99			var mu PaddedMutex
100			for atomic.AddInt32(&N, -1) >= 0 {
101				runtime.Gosched()
102				for g := 0; g < CallsPerSched; g++ {
103					mu.Lock()
104					mu.Unlock()
105				}
106			}
107			c <- true
108		}()
109	}
110	for p := 0; p < procs; p++ {
111		<-c
112	}
113}
114
115func benchmarkMutex(b *testing.B, slack, work bool) {
116	const (
117		CallsPerSched  = 1000
118		LocalWork      = 100
119		GoroutineSlack = 10
120	)
121	procs := runtime.GOMAXPROCS(-1)
122	if slack {
123		procs *= GoroutineSlack
124	}
125	N := int32(b.N / CallsPerSched)
126	c := make(chan bool, procs)
127	var mu Mutex
128	for p := 0; p < procs; p++ {
129		go func() {
130			foo := 0
131			for atomic.AddInt32(&N, -1) >= 0 {
132				runtime.Gosched()
133				for g := 0; g < CallsPerSched; g++ {
134					mu.Lock()
135					mu.Unlock()
136					if work {
137						for i := 0; i < LocalWork; i++ {
138							foo *= 2
139							foo /= 2
140						}
141					}
142				}
143			}
144			c <- foo == 42
145		}()
146	}
147	for p := 0; p < procs; p++ {
148		<-c
149	}
150}
151
152func BenchmarkMutex(b *testing.B) {
153	benchmarkMutex(b, false, false)
154}
155
156func BenchmarkMutexSlack(b *testing.B) {
157	benchmarkMutex(b, true, false)
158}
159
160func BenchmarkMutexWork(b *testing.B) {
161	benchmarkMutex(b, false, true)
162}
163
164func BenchmarkMutexWorkSlack(b *testing.B) {
165	benchmarkMutex(b, true, true)
166}
167