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