1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Copyright 2019 The gVisor Authors.
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
5 6 // This is mostly copied from the standard library's sync/rwmutex.go.
7 //
8 // Happens-before relationships indicated to the race detector:
9 // - Unlock -> Lock (via writerSem)
10 // - Unlock -> RLock (via readerSem)
11 // - RUnlock -> Lock (via writerSem)
12 // - DowngradeLock -> RLock (via readerSem)
13 14 package sync
15 16 import (
17 "sync/atomic"
18 "unsafe"
19 )
20 21 // CrossGoroutineRWMutex is equivalent to RWMutex, but it need not be unlocked
22 // by a the same goroutine that locked the mutex.
23 type CrossGoroutineRWMutex struct {
24 // w is held if there are pending writers
25 //
26 // We use CrossGoroutineMutex rather than Mutex because the lock
27 // annotation instrumentation in Mutex will trigger false positives in
28 // the race detector when called inside of RaceDisable.
29 w CrossGoroutineMutex
30 writerSem uint32 // semaphore for writers to wait for completing readers
31 readerSem uint32 // semaphore for readers to wait for completing writers
32 readerCount int32 // number of pending readers
33 readerWait int32 // number of departing readers
34 }
35 36 const rwmutexMaxReaders = 1 << 30
37 38 // TryRLock locks rw for reading. It returns true if it succeeds and false
39 // otherwise. It does not block.
40 // +checklocksignore
41 func (rw *CrossGoroutineRWMutex) TryRLock() bool {
42 if RaceEnabled {
43 RaceDisable()
44 }
45 for {
46 rc := atomic.LoadInt32(&rw.readerCount)
47 if rc < 0 {
48 if RaceEnabled {
49 RaceEnable()
50 }
51 return false
52 }
53 if !atomic.CompareAndSwapInt32(&rw.readerCount, rc, rc+1) {
54 continue
55 }
56 if RaceEnabled {
57 RaceEnable()
58 RaceAcquire(unsafe.Pointer(&rw.readerSem))
59 }
60 return true
61 }
62 }
63 64 // RLock locks rw for reading.
65 //
66 // It should not be used for recursive read locking; a blocked Lock call
67 // excludes new readers from acquiring the lock. See the documentation on the
68 // RWMutex type.
69 // +checklocksignore
70 func (rw *CrossGoroutineRWMutex) RLock() {
71 if RaceEnabled {
72 RaceDisable()
73 }
74 if atomic.AddInt32(&rw.readerCount, 1) < 0 {
75 // A writer is pending, wait for it.
76 semacquire(&rw.readerSem)
77 }
78 if RaceEnabled {
79 RaceEnable()
80 RaceAcquire(unsafe.Pointer(&rw.readerSem))
81 }
82 }
83 84 // RUnlock undoes a single RLock call.
85 //
86 // Preconditions:
87 // - rw is locked for reading.
88 //
89 // +checklocksignore
90 func (rw *CrossGoroutineRWMutex) RUnlock() {
91 if RaceEnabled {
92 RaceReleaseMerge(unsafe.Pointer(&rw.writerSem))
93 RaceDisable()
94 }
95 if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
96 if r+1 == 0 || r+1 == -rwmutexMaxReaders {
97 panic("RUnlock of unlocked RWMutex")
98 }
99 // A writer is pending.
100 if atomic.AddInt32(&rw.readerWait, -1) == 0 {
101 // The last reader unblocks the writer.
102 semrelease(&rw.writerSem, false, 0)
103 }
104 }
105 if RaceEnabled {
106 RaceEnable()
107 }
108 }
109 110 // TryLock locks rw for writing. It returns true if it succeeds and false
111 // otherwise. It does not block.
112 // +checklocksignore
113 func (rw *CrossGoroutineRWMutex) TryLock() bool {
114 if RaceEnabled {
115 RaceDisable()
116 }
117 // First, resolve competition with other writers.
118 if !rw.w.TryLock() {
119 if RaceEnabled {
120 RaceEnable()
121 }
122 return false
123 }
124 // Only proceed if there are no readers.
125 if !atomic.CompareAndSwapInt32(&rw.readerCount, 0, -rwmutexMaxReaders) {
126 rw.w.Unlock()
127 if RaceEnabled {
128 RaceEnable()
129 }
130 return false
131 }
132 if RaceEnabled {
133 RaceEnable()
134 RaceAcquire(unsafe.Pointer(&rw.writerSem))
135 }
136 return true
137 }
138 139 // Lock locks rw for writing. If the lock is already locked for reading or
140 // writing, Lock blocks until the lock is available.
141 // +checklocksignore
142 func (rw *CrossGoroutineRWMutex) Lock() {
143 if RaceEnabled {
144 RaceDisable()
145 }
146 // First, resolve competition with other writers.
147 rw.w.Lock()
148 // Announce to readers there is a pending writer.
149 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
150 // Wait for active readers.
151 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
152 semacquire(&rw.writerSem)
153 }
154 if RaceEnabled {
155 RaceEnable()
156 RaceAcquire(unsafe.Pointer(&rw.writerSem))
157 }
158 }
159 160 // Unlock unlocks rw for writing.
161 //
162 // Preconditions:
163 // - rw is locked for writing.
164 //
165 // +checklocksignore
166 func (rw *CrossGoroutineRWMutex) Unlock() {
167 if RaceEnabled {
168 RaceRelease(unsafe.Pointer(&rw.writerSem))
169 RaceRelease(unsafe.Pointer(&rw.readerSem))
170 RaceDisable()
171 }
172 // Announce to readers there is no active writer.
173 r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
174 if r >= rwmutexMaxReaders {
175 panic("Unlock of unlocked RWMutex")
176 }
177 // Unblock blocked readers, if any.
178 for i := 0; i < int(r); i++ {
179 semrelease(&rw.readerSem, false, 0)
180 }
181 // Allow other writers to proceed.
182 rw.w.Unlock()
183 if RaceEnabled {
184 RaceEnable()
185 }
186 }
187 188 // DowngradeLock atomically unlocks rw for writing and locks it for reading.
189 //
190 // Preconditions:
191 // - rw is locked for writing.
192 //
193 // +checklocksignore
194 func (rw *CrossGoroutineRWMutex) DowngradeLock() {
195 if RaceEnabled {
196 RaceRelease(unsafe.Pointer(&rw.readerSem))
197 RaceDisable()
198 }
199 // Announce to readers there is no active writer and one additional reader.
200 r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders+1)
201 if r >= rwmutexMaxReaders+1 {
202 panic("DowngradeLock of unlocked RWMutex")
203 }
204 // Unblock blocked readers, if any. Note that this loop starts as 1 since r
205 // includes this goroutine.
206 for i := 1; i < int(r); i++ {
207 semrelease(&rw.readerSem, false, 0)
208 }
209 // Allow other writers to proceed to rw.w.Lock(). Note that they will still
210 // block on rw.writerSem since at least this reader exists, such that
211 // DowngradeLock() is atomic with the previous write lock.
212 rw.w.Unlock()
213 if RaceEnabled {
214 RaceEnable()
215 }
216 }
217 218 // A RWMutex is a reader/writer mutual exclusion lock. The lock can be held by
219 // an arbitrary number of readers or a single writer. The zero value for a
220 // RWMutex is an unlocked mutex.
221 //
222 // A RWMutex must not be copied after first use.
223 //
224 // If a goroutine holds a RWMutex for reading and another goroutine might call
225 // Lock, no goroutine should expect to be able to acquire a read lock until the
226 // initial read lock is released. In particular, this prohibits recursive read
227 // locking. This is to ensure that the lock eventually becomes available; a
228 // blocked Lock call excludes new readers from acquiring the lock.
229 //
230 // A Mutex must be unlocked by the same goroutine that locked it. This
231 // invariant is enforced with the 'checklocks' build tag.
232 type RWMutex struct {
233 m CrossGoroutineRWMutex
234 }
235 236 // TryRLock locks rw for reading. It returns true if it succeeds and false
237 // otherwise. It does not block.
238 // +checklocksignore
239 func (rw *RWMutex) TryRLock() bool {
240 // Note lock first to enforce proper locking even if unsuccessful.
241 noteLock(unsafe.Pointer(rw))
242 locked := rw.m.TryRLock()
243 if !locked {
244 noteUnlock(unsafe.Pointer(rw))
245 }
246 return locked
247 }
248 249 // RLock locks rw for reading.
250 //
251 // It should not be used for recursive read locking; a blocked Lock call
252 // excludes new readers from acquiring the lock. See the documentation on the
253 // RWMutex type.
254 // +checklocksignore
255 func (rw *RWMutex) RLock() {
256 noteLock(unsafe.Pointer(rw))
257 rw.m.RLock()
258 }
259 260 // RUnlock undoes a single RLock call.
261 //
262 // Preconditions:
263 // - rw is locked for reading.
264 // - rw was locked by this goroutine.
265 //
266 // +checklocksignore
267 func (rw *RWMutex) RUnlock() {
268 rw.m.RUnlock()
269 noteUnlock(unsafe.Pointer(rw))
270 }
271 272 // TryLock locks rw for writing. It returns true if it succeeds and false
273 // otherwise. It does not block.
274 // +checklocksignore
275 func (rw *RWMutex) TryLock() bool {
276 // Note lock first to enforce proper locking even if unsuccessful.
277 noteLock(unsafe.Pointer(rw))
278 locked := rw.m.TryLock()
279 if !locked {
280 noteUnlock(unsafe.Pointer(rw))
281 }
282 return locked
283 }
284 285 // Lock locks rw for writing. If the lock is already locked for reading or
286 // writing, Lock blocks until the lock is available.
287 // +checklocksignore
288 func (rw *RWMutex) Lock() {
289 noteLock(unsafe.Pointer(rw))
290 rw.m.Lock()
291 }
292 293 // Unlock unlocks rw for writing.
294 //
295 // Preconditions:
296 // - rw is locked for writing.
297 // - rw was locked by this goroutine.
298 //
299 // +checklocksignore
300 func (rw *RWMutex) Unlock() {
301 rw.m.Unlock()
302 noteUnlock(unsafe.Pointer(rw))
303 }
304 305 // DowngradeLock atomically unlocks rw for writing and locks it for reading.
306 //
307 // Preconditions:
308 // - rw is locked for writing.
309 //
310 // +checklocksignore
311 func (rw *RWMutex) DowngradeLock() {
312 // No note change for DowngradeLock.
313 rw.m.DowngradeLock()
314 }
315