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	"fmt"
11	"runtime"
12	. "sync"
13	"sync/atomic"
14	"testing"
15)
16
17// There is a modified copy of this file in runtime/rwmutex_test.go.
18// If you make any changes here, see if you should make them there.
19
20func parallelReader(m *RWMutex, clocked, cunlock, cdone chan bool) {
21	m.RLock()
22	clocked <- true
23	<-cunlock
24	m.RUnlock()
25	cdone <- true
26}
27
28func doTestParallelReaders(numReaders, gomaxprocs int) {
29	runtime.GOMAXPROCS(gomaxprocs)
30	var m RWMutex
31	clocked := make(chan bool)
32	cunlock := make(chan bool)
33	cdone := make(chan bool)
34	for i := 0; i < numReaders; i++ {
35		go parallelReader(&m, clocked, cunlock, cdone)
36	}
37	// Wait for all parallel RLock()s to succeed.
38	for i := 0; i < numReaders; i++ {
39		<-clocked
40	}
41	for i := 0; i < numReaders; i++ {
42		cunlock <- true
43	}
44	// Wait for the goroutines to finish.
45	for i := 0; i < numReaders; i++ {
46		<-cdone
47	}
48}
49
50func TestParallelReaders(t *testing.T) {
51	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
52	doTestParallelReaders(1, 4)
53	doTestParallelReaders(3, 4)
54	doTestParallelReaders(4, 2)
55}
56
57func reader(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool) {
58	for i := 0; i < num_iterations; i++ {
59		rwm.RLock()
60		n := atomic.AddInt32(activity, 1)
61		if n < 1 || n >= 10000 {
62			rwm.RUnlock()
63			panic(fmt.Sprintf("wlock(%d)\n", n))
64		}
65		for i := 0; i < 100; i++ {
66		}
67		atomic.AddInt32(activity, -1)
68		rwm.RUnlock()
69	}
70	cdone <- true
71}
72
73func writer(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool) {
74	for i := 0; i < num_iterations; i++ {
75		rwm.Lock()
76		n := atomic.AddInt32(activity, 10000)
77		if n != 10000 {
78			rwm.Unlock()
79			panic(fmt.Sprintf("wlock(%d)\n", n))
80		}
81		for i := 0; i < 100; i++ {
82		}
83		atomic.AddInt32(activity, -10000)
84		rwm.Unlock()
85	}
86	cdone <- true
87}
88
89func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
90	runtime.GOMAXPROCS(gomaxprocs)
91	// Number of active readers + 10000 * number of active writers.
92	var activity int32
93	var rwm RWMutex
94	cdone := make(chan bool)
95	go writer(&rwm, num_iterations, &activity, cdone)
96	var i int
97	for i = 0; i < numReaders/2; i++ {
98		go reader(&rwm, num_iterations, &activity, cdone)
99	}
100	go writer(&rwm, num_iterations, &activity, cdone)
101	for ; i < numReaders; i++ {
102		go reader(&rwm, num_iterations, &activity, cdone)
103	}
104	// Wait for the 2 writers and all readers to finish.
105	for i := 0; i < 2+numReaders; i++ {
106		<-cdone
107	}
108}
109
110func TestRWMutex(t *testing.T) {
111	var m RWMutex
112
113	m.Lock()
114	if m.TryLock() {
115		t.Fatalf("TryLock succeeded with mutex locked")
116	}
117	if m.TryRLock() {
118		t.Fatalf("TryRLock succeeded with mutex locked")
119	}
120	m.Unlock()
121
122	if !m.TryLock() {
123		t.Fatalf("TryLock failed with mutex unlocked")
124	}
125	m.Unlock()
126
127	if !m.TryRLock() {
128		t.Fatalf("TryRLock failed with mutex unlocked")
129	}
130	if !m.TryRLock() {
131		t.Fatalf("TryRLock failed with mutex rlocked")
132	}
133	if m.TryLock() {
134		t.Fatalf("TryLock succeeded with mutex rlocked")
135	}
136	m.RUnlock()
137	m.RUnlock()
138
139	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
140	n := 1000
141	if testing.Short() {
142		n = 5
143	}
144	HammerRWMutex(1, 1, n)
145	HammerRWMutex(1, 3, n)
146	HammerRWMutex(1, 10, n)
147	HammerRWMutex(4, 1, n)
148	HammerRWMutex(4, 3, n)
149	HammerRWMutex(4, 10, n)
150	HammerRWMutex(10, 1, n)
151	HammerRWMutex(10, 3, n)
152	HammerRWMutex(10, 10, n)
153	HammerRWMutex(10, 5, n)
154}
155
156func TestRLocker(t *testing.T) {
157	var wl RWMutex
158	var rl Locker
159	wlocked := make(chan bool, 1)
160	rlocked := make(chan bool, 1)
161	rl = wl.RLocker()
162	n := 10
163	go func() {
164		for i := 0; i < n; i++ {
165			rl.Lock()
166			rl.Lock()
167			rlocked <- true
168			wl.Lock()
169			wlocked <- true
170		}
171	}()
172	for i := 0; i < n; i++ {
173		<-rlocked
174		rl.Unlock()
175		select {
176		case <-wlocked:
177			t.Fatal("RLocker() didn't read-lock it")
178		default:
179		}
180		rl.Unlock()
181		<-wlocked
182		select {
183		case <-rlocked:
184			t.Fatal("RLocker() didn't respect the write lock")
185		default:
186		}
187		wl.Unlock()
188	}
189}
190
191func BenchmarkRWMutexUncontended(b *testing.B) {
192	type PaddedRWMutex struct {
193		RWMutex
194		pad [32]uint32
195	}
196	b.RunParallel(func(pb *testing.PB) {
197		var rwm PaddedRWMutex
198		for pb.Next() {
199			rwm.RLock()
200			rwm.RLock()
201			rwm.RUnlock()
202			rwm.RUnlock()
203			rwm.Lock()
204			rwm.Unlock()
205		}
206	})
207}
208
209func benchmarkRWMutex(b *testing.B, localWork, writeRatio int) {
210	var rwm RWMutex
211	b.RunParallel(func(pb *testing.PB) {
212		foo := 0
213		for pb.Next() {
214			foo++
215			if foo%writeRatio == 0 {
216				rwm.Lock()
217				rwm.Unlock()
218			} else {
219				rwm.RLock()
220				for i := 0; i != localWork; i += 1 {
221					foo *= 2
222					foo /= 2
223				}
224				rwm.RUnlock()
225			}
226		}
227		_ = foo
228	})
229}
230
231func BenchmarkRWMutexWrite100(b *testing.B) {
232	benchmarkRWMutex(b, 0, 100)
233}
234
235func BenchmarkRWMutexWrite10(b *testing.B) {
236	benchmarkRWMutex(b, 0, 10)
237}
238
239func BenchmarkRWMutexWorkWrite100(b *testing.B) {
240	benchmarkRWMutex(b, 100, 100)
241}
242
243func BenchmarkRWMutexWorkWrite10(b *testing.B) {
244	benchmarkRWMutex(b, 100, 10)
245}
246