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