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 // RUnlock undoes a single RLock call; 72 // it does not affect other simultaneous readers. 73 // It is a run-time error if rw is not locked for reading 74 // on entry to RUnlock. 75 func (rw *RWMutex) RUnlock() { 76 if race.Enabled { 77 _ = rw.w.state 78 race.ReleaseMerge(unsafe.Pointer(&rw.writerSem)) 79 race.Disable() 80 } 81 if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { 82 // Outlined slow-path to allow the fast-path to be inlined 83 rw.rUnlockSlow(r) 84 } 85 if race.Enabled { 86 race.Enable() 87 } 88 } 89 90 func (rw *RWMutex) rUnlockSlow(r int32) { 91 if r+1 == 0 || r+1 == -rwmutexMaxReaders { 92 race.Enable() 93 throw("sync: RUnlock of unlocked RWMutex") 94 } 95 // A writer is pending. 96 if atomic.AddInt32(&rw.readerWait, -1) == 0 { 97 // The last reader unblocks the writer. 98 runtime_Semrelease(&rw.writerSem, false, 1) 99 } 100 } 101 102 // Lock locks rw for writing. 103 // If the lock is already locked for reading or writing, 104 // Lock blocks until the lock is available. 105 func (rw *RWMutex) Lock() { 106 if race.Enabled { 107 _ = rw.w.state 108 race.Disable() 109 } 110 // First, resolve competition with other writers. 111 rw.w.Lock() 112 // Announce to readers there is a pending writer. 113 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders 114 // Wait for active readers. 115 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { 116 runtime_SemacquireMutex(&rw.writerSem, false, 0) 117 } 118 if race.Enabled { 119 race.Enable() 120 race.Acquire(unsafe.Pointer(&rw.readerSem)) 121 race.Acquire(unsafe.Pointer(&rw.writerSem)) 122 } 123 } 124 125 // Unlock unlocks rw for writing. It is a run-time error if rw is 126 // not locked for writing on entry to Unlock. 127 // 128 // As with Mutexes, a locked RWMutex is not associated with a particular 129 // goroutine. One goroutine may RLock (Lock) a RWMutex and then 130 // arrange for another goroutine to RUnlock (Unlock) it. 131 func (rw *RWMutex) Unlock() { 132 if race.Enabled { 133 _ = rw.w.state 134 race.Release(unsafe.Pointer(&rw.readerSem)) 135 race.Disable() 136 } 137 138 // Announce to readers there is no active writer. 139 r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) 140 if r >= rwmutexMaxReaders { 141 race.Enable() 142 throw("sync: Unlock of unlocked RWMutex") 143 } 144 // Unblock blocked readers, if any. 145 for i := 0; i < int(r); i++ { 146 runtime_Semrelease(&rw.readerSem, false, 0) 147 } 148 // Allow other writers to proceed. 149 rw.w.Unlock() 150 if race.Enabled { 151 race.Enable() 152 } 153 } 154 155 // RLocker returns a Locker interface that implements 156 // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock. 157 func (rw *RWMutex) RLocker() Locker { 158 return (*rlocker)(rw) 159 } 160 161 type rlocker RWMutex 162 163 func (r *rlocker) Lock() { (*RWMutex)(r).RLock() } 164 func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() } 165