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 package sync 6 7 import ( 8 "internal/race" 9 "sync/atomic" 10 "unsafe" 11 ) 12 13 // There is a modified copy of this file in runtime/rwmutex.go. 14 // If you make any changes here, see if you should make them there. 15 16 // A RWMutex is a reader/writer mutual exclusion lock. 17 // The lock can be held by an arbitrary number of readers or a single writer. 18 // The zero value for a RWMutex is an unlocked mutex. 19 // 20 // A RWMutex must not be copied after first use. 21 // 22 // If any goroutine calls Lock while the lock is already held by 23 // one or more readers, concurrent calls to RLock will block until 24 // the writer has acquired (and released) the lock, to ensure that 25 // the lock eventually becomes available to the writer. 26 // Note that this prohibits recursive read-locking. 27 // 28 // In the terminology of the Go memory model, 29 // the n'th call to Unlock “synchronizes before” the m'th call to Lock 30 // for any n < m, just as for Mutex. 31 // For any call to RLock, there exists an n such that 32 // the n'th call to Unlock “synchronizes before” that call to RLock, 33 // and the corresponding call to RUnlock “synchronizes before” 34 // the n+1'th call to Lock. 35 type RWMutex struct { 36 w Mutex // held if there are pending writers 37 writerSem uint32 // semaphore for writers to wait for completing readers 38 readerSem uint32 // semaphore for readers to wait for completing writers 39 readerCount atomic.Int32 // number of pending readers 40 readerWait atomic.Int32 // number of departing readers 41 } 42 43 const rwmutexMaxReaders = 1 << 30 44 45 // Happens-before relationships are indicated to the race detector via: 46 // - Unlock -> Lock: readerSem 47 // - Unlock -> RLock: readerSem 48 // - RUnlock -> Lock: writerSem 49 // 50 // The methods below temporarily disable handling of race synchronization 51 // events in order to provide the more precise model above to the race 52 // detector. 53 // 54 // For example, atomic.AddInt32 in RLock should not appear to provide 55 // acquire-release semantics, which would incorrectly synchronize racing 56 // readers, thus potentially missing races. 57 58 // RLock locks rw for reading. 59 // 60 // It should not be used for recursive read locking; a blocked Lock 61 // call excludes new readers from acquiring the lock. See the 62 // documentation on the RWMutex type. 63 func (rw *RWMutex) RLock() { 64 if race.Enabled { 65 _ = rw.w.state 66 race.Disable() 67 } 68 if rw.readerCount.Add(1) < 0 { 69 // A writer is pending, wait for it. 70 runtime_SemacquireRWMutexR(&rw.readerSem, false, 0) 71 } 72 if race.Enabled { 73 race.Enable() 74 race.Acquire(unsafe.Pointer(&rw.readerSem)) 75 } 76 } 77 78 // TryRLock tries to lock rw for reading and reports whether it succeeded. 79 // 80 // Note that while correct uses of TryRLock do exist, they are rare, 81 // and use of TryRLock is often a sign of a deeper problem 82 // in a particular use of mutexes. 83 func (rw *RWMutex) TryRLock() bool { 84 if race.Enabled { 85 _ = rw.w.state 86 race.Disable() 87 } 88 for { 89 c := rw.readerCount.Load() 90 if c < 0 { 91 if race.Enabled { 92 race.Enable() 93 } 94 return false 95 } 96 if rw.readerCount.CompareAndSwap(c, c+1) { 97 if race.Enabled { 98 race.Enable() 99 race.Acquire(unsafe.Pointer(&rw.readerSem)) 100 } 101 return true 102 } 103 } 104 } 105 106 // RUnlock undoes a single RLock call; 107 // it does not affect other simultaneous readers. 108 // It is a run-time error if rw is not locked for reading 109 // on entry to RUnlock. 110 func (rw *RWMutex) RUnlock() { 111 if race.Enabled { 112 _ = rw.w.state 113 race.ReleaseMerge(unsafe.Pointer(&rw.writerSem)) 114 race.Disable() 115 } 116 if r := rw.readerCount.Add(-1); r < 0 { 117 // Outlined slow-path to allow the fast-path to be inlined 118 rw.rUnlockSlow(r) 119 } 120 if race.Enabled { 121 race.Enable() 122 } 123 } 124 125 func (rw *RWMutex) rUnlockSlow(r int32) { 126 if r+1 == 0 || r+1 == -rwmutexMaxReaders { 127 race.Enable() 128 fatal("sync: RUnlock of unlocked RWMutex") 129 } 130 // A writer is pending. 131 if rw.readerWait.Add(-1) == 0 { 132 // The last reader unblocks the writer. 133 runtime_Semrelease(&rw.writerSem, false, 1) 134 } 135 } 136 137 // Lock locks rw for writing. 138 // If the lock is already locked for reading or writing, 139 // Lock blocks until the lock is available. 140 func (rw *RWMutex) Lock() { 141 if race.Enabled { 142 _ = rw.w.state 143 race.Disable() 144 } 145 // First, resolve competition with other writers. 146 rw.w.Lock() 147 // Announce to readers there is a pending writer. 148 r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders 149 // Wait for active readers. 150 if r != 0 && rw.readerWait.Add(r) != 0 { 151 runtime_SemacquireRWMutex(&rw.writerSem, false, 0) 152 } 153 if race.Enabled { 154 race.Enable() 155 race.Acquire(unsafe.Pointer(&rw.readerSem)) 156 race.Acquire(unsafe.Pointer(&rw.writerSem)) 157 } 158 } 159 160 // TryLock tries to lock rw for writing and reports whether it succeeded. 161 // 162 // Note that while correct uses of TryLock do exist, they are rare, 163 // and use of TryLock is often a sign of a deeper problem 164 // in a particular use of mutexes. 165 func (rw *RWMutex) TryLock() bool { 166 if race.Enabled { 167 _ = rw.w.state 168 race.Disable() 169 } 170 if !rw.w.TryLock() { 171 if race.Enabled { 172 race.Enable() 173 } 174 return false 175 } 176 if !rw.readerCount.CompareAndSwap(0, -rwmutexMaxReaders) { 177 rw.w.Unlock() 178 if race.Enabled { 179 race.Enable() 180 } 181 return false 182 } 183 if race.Enabled { 184 race.Enable() 185 race.Acquire(unsafe.Pointer(&rw.readerSem)) 186 race.Acquire(unsafe.Pointer(&rw.writerSem)) 187 } 188 return true 189 } 190 191 // Unlock unlocks rw for writing. It is a run-time error if rw is 192 // not locked for writing on entry to Unlock. 193 // 194 // As with Mutexes, a locked RWMutex is not associated with a particular 195 // goroutine. One goroutine may RLock (Lock) a RWMutex and then 196 // arrange for another goroutine to RUnlock (Unlock) it. 197 func (rw *RWMutex) Unlock() { 198 if race.Enabled { 199 _ = rw.w.state 200 race.Release(unsafe.Pointer(&rw.readerSem)) 201 race.Disable() 202 } 203 204 // Announce to readers there is no active writer. 205 r := rw.readerCount.Add(rwmutexMaxReaders) 206 if r >= rwmutexMaxReaders { 207 race.Enable() 208 fatal("sync: Unlock of unlocked RWMutex") 209 } 210 // Unblock blocked readers, if any. 211 for i := 0; i < int(r); i++ { 212 runtime_Semrelease(&rw.readerSem, false, 0) 213 } 214 // Allow other writers to proceed. 215 rw.w.Unlock() 216 if race.Enabled { 217 race.Enable() 218 } 219 } 220 221 // syscall_hasWaitingReaders reports whether any goroutine is waiting 222 // to acquire a read lock on rw. This exists because syscall.ForkLock 223 // is an RWMutex, and we can't change that without breaking compatibility. 224 // We don't need or want RWMutex semantics for ForkLock, and we use 225 // this private API to avoid having to change the type of ForkLock. 226 // For more details see the syscall package. 227 // 228 //go:linkname syscall_hasWaitingReaders syscall.hasWaitingReaders 229 func syscall_hasWaitingReaders(rw *RWMutex) bool { 230 r := rw.readerCount.Load() 231 return r < 0 && r+rwmutexMaxReaders > 0 232 } 233 234 // RLocker returns a Locker interface that implements 235 // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock. 236 func (rw *RWMutex) RLocker() Locker { 237 return (*rlocker)(rw) 238 } 239 240 type rlocker RWMutex 241 242 func (r *rlocker) Lock() { (*RWMutex)(r).RLock() } 243 func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() } 244