Source file
src/database/sql/closemu_test.go
1
2
3
4
5 package sql
6
7 import (
8 "runtime"
9 "testing"
10 "testing/synctest"
11 "time"
12 )
13
14 func TestClosingMutex(t *testing.T) {
15 start := func(t *testing.T, f func()) func() bool {
16 done := false
17 go func() {
18 f()
19 done = true
20 }()
21 return func() bool {
22 synctest.Wait()
23 return done
24 }
25 }
26
27 synctest.Test(t, func(t *testing.T) {
28 var m closingMutex
29
30
31 m.RLock()
32 m.RLock()
33 m.RUnlock()
34 m.RUnlock()
35
36
37 m.RLock()
38 lock1Done := start(t, m.Lock)
39 if lock1Done() {
40 t.Fatalf("m.Lock(): succeeded on RLocked mutex")
41 }
42 m.RLock()
43 m.RUnlock()
44 if lock1Done() {
45 t.Fatalf("m.Lock(): succeeded after one RUnlock, one RLock remains")
46 }
47 m.RUnlock()
48 if !lock1Done() {
49 t.Fatalf("m.Lock(): still blocking after all RUnlocks")
50 }
51 m.Unlock()
52
53
54 m.Lock()
55 rlock1Done := start(t, m.RLock)
56 rlock2Done := start(t, m.RLock)
57 if rlock1Done() || rlock2Done() {
58 t.Fatalf("m.RLock(): succeeded on Locked mutex")
59 }
60 m.Unlock()
61 if !rlock1Done() || !rlock2Done() {
62 t.Fatalf("m.RLock(): succeeded on Locked mutex")
63 }
64 m.RUnlock()
65 m.RUnlock()
66
67
68 m.Lock()
69 lock2Done := start(t, m.Lock)
70 if lock2Done() {
71 t.Fatalf("m.Lock(): succeeded on Locked mutex")
72 }
73 m.Unlock()
74 if !lock2Done() {
75 t.Fatalf("m.Lock(): still blocking after Unlock")
76 }
77 m.Unlock()
78
79
80 m.RLock()
81 lock3Done := start(t, m.Lock)
82 if lock3Done() {
83 t.Fatalf("m.Lock(): succeeded on RLocked mutex")
84 }
85 m.RLock()
86 m.RUnlock()
87 m.RUnlock()
88 if !lock3Done() {
89 t.Fatalf("m.Lock(): still blocking after RUnlock")
90 }
91 m.Unlock()
92 })
93 }
94
95 func TestClosingMutexLockStarvation(t *testing.T) {
96 synctest.Test(t, func(t *testing.T) {
97
98 for range 100 {
99 var m closingMutex
100
101
102 m.RLock()
103 locked := false
104 go func() {
105 m.Lock()
106 locked = true
107 m.Unlock()
108 }()
109 synctest.Wait()
110 if locked {
111 t.Errorf("lock acquired while mutex is rlocked")
112 }
113
114
115 m.RLock()
116 m.RUnlock()
117 if locked {
118 t.Errorf("lock acquired while mutex is double-rlocked")
119 }
120
121
122
123
124 m.RUnlock()
125 m.RLock()
126 if !locked {
127 t.Errorf("lock not acquired when rlock dropped")
128 }
129 m.RUnlock()
130 }
131 })
132 }
133
134 func TestClosingMutexLockRLockRace(t *testing.T) {
135 oldProcs := runtime.GOMAXPROCS(2)
136 defer runtime.GOMAXPROCS(oldProcs)
137
138
139
140
141 var m closingMutex
142 done := make(chan struct{})
143 go func() {
144 defer close(done)
145 for range 10_000 {
146 m.Lock()
147 m.Unlock()
148 }
149 }()
150
151
152
153 for range 100_000 {
154 select {
155 case <-done:
156 return
157 default:
158 }
159 if m.TryRLock() {
160 m.RUnlock()
161 }
162 runtime.Gosched()
163 }
164 select {
165 case <-done:
166 case <-time.After(time.Minute):
167 if state := m.state.Load(); state == 1 {
168 t.Fatalf("Lock left the mutex in state 1 with no reader to wake the writer")
169 }
170 t.Fatalf("Lock did not complete within one minute; mutex state is %v", m.state.Load())
171 }
172 }
173
174 func TestClosingMutexPanics(t *testing.T) {
175 for _, test := range []struct {
176 name string
177 f func()
178 }{{
179 name: "double RUnlock",
180 f: func() {
181 var m closingMutex
182 m.RLock()
183 m.RUnlock()
184 m.RUnlock()
185 },
186 }, {
187 name: "double Unlock",
188 f: func() {
189 var m closingMutex
190 m.Lock()
191 m.Unlock()
192 m.Unlock()
193 },
194 }} {
195 var got any
196 func() {
197 defer func() {
198 got = recover()
199 }()
200 test.f()
201 }()
202 if got == nil {
203 t.Errorf("no panic, want one")
204 }
205 }
206 }
207
View as plain text