|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package sync | 
|  |  | 
|  | import ( | 
|  | "internal/race" | 
|  | "sync/atomic" | 
|  | "unsafe" | 
|  | ) | 
|  |  | 
|  | // There is a modified copy of this file in runtime/rwmutex.go. | 
|  | // If you make any changes here, see if you should make them there. | 
|  |  | 
|  | // A RWMutex is a reader/writer mutual exclusion lock. | 
|  | // The lock can be held by an arbitrary number of readers or a single writer. | 
|  | // The zero value for a RWMutex is an unlocked mutex. | 
|  | // | 
|  | // A RWMutex must not be copied after first use. | 
|  | // | 
|  | // If a goroutine holds a RWMutex for reading and another goroutine might | 
|  | // call Lock, no goroutine should expect to be able to acquire a read lock | 
|  | // until the initial read lock is released. In particular, this prohibits | 
|  | // recursive read locking. This is to ensure that the lock eventually becomes | 
|  | // available; a blocked Lock call excludes new readers from acquiring the | 
|  | // lock. | 
|  | type RWMutex struct { | 
|  | w           Mutex  // held if there are pending writers | 
|  | writerSem   uint32 // semaphore for writers to wait for completing readers | 
|  | readerSem   uint32 // semaphore for readers to wait for completing writers | 
|  | readerCount int32  // number of pending readers | 
|  | readerWait  int32  // number of departing readers | 
|  | } | 
|  |  | 
|  | const rwmutexMaxReaders = 1 << 30 | 
|  |  | 
|  | // RLock locks rw for reading. | 
|  | // | 
|  | // It should not be used for recursive read locking; a blocked Lock | 
|  | // call excludes new readers from acquiring the lock. See the | 
|  | // documentation on the RWMutex type. | 
|  | func (rw *RWMutex) RLock() { | 
|  | if race.Enabled { | 
|  | _ = rw.w.state | 
|  | race.Disable() | 
|  | } | 
|  | if atomic.AddInt32(&rw.readerCount, 1) < 0 { | 
|  | // A writer is pending, wait for it. | 
|  | runtime_SemacquireMutex(&rw.readerSem, false) | 
|  | } | 
|  | if race.Enabled { | 
|  | race.Enable() | 
|  | race.Acquire(unsafe.Pointer(&rw.readerSem)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // RUnlock undoes a single RLock call; | 
|  | // it does not affect other simultaneous readers. | 
|  | // It is a run-time error if rw is not locked for reading | 
|  | // on entry to RUnlock. | 
|  | func (rw *RWMutex) RUnlock() { | 
|  | if race.Enabled { | 
|  | _ = rw.w.state | 
|  | race.ReleaseMerge(unsafe.Pointer(&rw.writerSem)) | 
|  | race.Disable() | 
|  | } | 
|  | if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { | 
|  | if r+1 == 0 || r+1 == -rwmutexMaxReaders { | 
|  | race.Enable() | 
|  | throw("sync: RUnlock of unlocked RWMutex") | 
|  | } | 
|  | // A writer is pending. | 
|  | if atomic.AddInt32(&rw.readerWait, -1) == 0 { | 
|  | // The last reader unblocks the writer. | 
|  | runtime_Semrelease(&rw.writerSem, false) | 
|  | } | 
|  | } | 
|  | if race.Enabled { | 
|  | race.Enable() | 
|  | } | 
|  | } | 
|  |  | 
|  | // Lock locks rw for writing. | 
|  | // If the lock is already locked for reading or writing, | 
|  | // Lock blocks until the lock is available. | 
|  | func (rw *RWMutex) Lock() { | 
|  | if race.Enabled { | 
|  | _ = rw.w.state | 
|  | race.Disable() | 
|  | } | 
|  | // First, resolve competition with other writers. | 
|  | rw.w.Lock() | 
|  | // Announce to readers there is a pending writer. | 
|  | r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders | 
|  | // Wait for active readers. | 
|  | if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { | 
|  | runtime_SemacquireMutex(&rw.writerSem, false) | 
|  | } | 
|  | if race.Enabled { | 
|  | race.Enable() | 
|  | race.Acquire(unsafe.Pointer(&rw.readerSem)) | 
|  | race.Acquire(unsafe.Pointer(&rw.writerSem)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Unlock unlocks rw for writing. It is a run-time error if rw is | 
|  | // not locked for writing on entry to Unlock. | 
|  | // | 
|  | // As with Mutexes, a locked RWMutex is not associated with a particular | 
|  | // goroutine. One goroutine may RLock (Lock) a RWMutex and then | 
|  | // arrange for another goroutine to RUnlock (Unlock) it. | 
|  | func (rw *RWMutex) Unlock() { | 
|  | if race.Enabled { | 
|  | _ = rw.w.state | 
|  | race.Release(unsafe.Pointer(&rw.readerSem)) | 
|  | race.Disable() | 
|  | } | 
|  |  | 
|  | // Announce to readers there is no active writer. | 
|  | r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) | 
|  | if r >= rwmutexMaxReaders { | 
|  | race.Enable() | 
|  | throw("sync: Unlock of unlocked RWMutex") | 
|  | } | 
|  | // Unblock blocked readers, if any. | 
|  | for i := 0; i < int(r); i++ { | 
|  | runtime_Semrelease(&rw.readerSem, false) | 
|  | } | 
|  | // Allow other writers to proceed. | 
|  | rw.w.Unlock() | 
|  | if race.Enabled { | 
|  | race.Enable() | 
|  | } | 
|  | } | 
|  |  | 
|  | // RLocker returns a Locker interface that implements | 
|  | // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock. | 
|  | func (rw *RWMutex) RLocker() Locker { | 
|  | return (*rlocker)(rw) | 
|  | } | 
|  |  | 
|  | type rlocker RWMutex | 
|  |  | 
|  | func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() } | 
|  | func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() } |