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 5package sync 6 7import ( 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. 28type 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 36const rwmutexMaxReaders = 1 << 30 37 38// RLock locks rw for reading. 39// 40// It should not be used for recursive read locking; a blocked Lock 41// call excludes new readers from acquiring the lock. See the 42// documentation on the RWMutex type. 43func (rw *RWMutex) RLock() { 44 if race.Enabled { 45 _ = rw.w.state 46 race.Disable() 47 } 48 if atomic.AddInt32(&rw.readerCount, 1) < 0 { 49 // A writer is pending, wait for it. 50 runtime_SemacquireMutex(&rw.readerSem, false, 0) 51 } 52 if race.Enabled { 53 race.Enable() 54 race.Acquire(unsafe.Pointer(&rw.readerSem)) 55 } 56} 57 58// RUnlock undoes a single RLock call; 59// it does not affect other simultaneous readers. 60// It is a run-time error if rw is not locked for reading 61// on entry to RUnlock. 62func (rw *RWMutex) RUnlock() { 63 if race.Enabled { 64 _ = rw.w.state 65 race.ReleaseMerge(unsafe.Pointer(&rw.writerSem)) 66 race.Disable() 67 } 68 if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { 69 // Outlined slow-path to allow the fast-path to be inlined 70 rw.rUnlockSlow(r) 71 } 72 if race.Enabled { 73 race.Enable() 74 } 75} 76 77func (rw *RWMutex) rUnlockSlow(r int32) { 78 if r+1 == 0 || r+1 == -rwmutexMaxReaders { 79 race.Enable() 80 throw("sync: RUnlock of unlocked RWMutex") 81 } 82 // A writer is pending. 83 if atomic.AddInt32(&rw.readerWait, -1) == 0 { 84 // The last reader unblocks the writer. 85 runtime_Semrelease(&rw.writerSem, false, 1) 86 } 87} 88 89// Lock locks rw for writing. 90// If the lock is already locked for reading or writing, 91// Lock blocks until the lock is available. 92func (rw *RWMutex) Lock() { 93 if race.Enabled { 94 _ = rw.w.state 95 race.Disable() 96 } 97 // First, resolve competition with other writers. 98 rw.w.Lock() 99 // Announce to readers there is a pending writer. 100 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders 101 // Wait for active readers. 102 if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { 103 runtime_SemacquireMutex(&rw.writerSem, false, 0) 104 } 105 if race.Enabled { 106 race.Enable() 107 race.Acquire(unsafe.Pointer(&rw.readerSem)) 108 race.Acquire(unsafe.Pointer(&rw.writerSem)) 109 } 110} 111 112// Unlock unlocks rw for writing. It is a run-time error if rw is 113// not locked for writing on entry to Unlock. 114// 115// As with Mutexes, a locked RWMutex is not associated with a particular 116// goroutine. One goroutine may RLock (Lock) a RWMutex and then 117// arrange for another goroutine to RUnlock (Unlock) it. 118func (rw *RWMutex) Unlock() { 119 if race.Enabled { 120 _ = rw.w.state 121 race.Release(unsafe.Pointer(&rw.readerSem)) 122 race.Disable() 123 } 124 125 // Announce to readers there is no active writer. 126 r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) 127 if r >= rwmutexMaxReaders { 128 race.Enable() 129 throw("sync: Unlock of unlocked RWMutex") 130 } 131 // Unblock blocked readers, if any. 132 for i := 0; i < int(r); i++ { 133 runtime_Semrelease(&rw.readerSem, false, 0) 134 } 135 // Allow other writers to proceed. 136 rw.w.Unlock() 137 if race.Enabled { 138 race.Enable() 139 } 140} 141 142// RLocker returns a Locker interface that implements 143// the Lock and Unlock methods by calling rw.RLock and rw.RUnlock. 144func (rw *RWMutex) RLocker() Locker { 145 return (*rlocker)(rw) 146} 147 148type rlocker RWMutex 149 150func (r *rlocker) Lock() { (*RWMutex)(r).RLock() } 151func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() } 152