Source File
rwmutex.go
Belonging Package
sync
// 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 syncimport ()// 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 any goroutine calls [RWMutex.Lock] while the lock is already held by// one or more readers, concurrent calls to [RWMutex.RLock] will block until// the writer has acquired (and released) the lock, to ensure that// the lock eventually becomes available to the writer.// Note that this prohibits recursive read-locking.// A [RWMutex.RLock] cannot be upgraded into a [RWMutex.Lock],// nor can a [RWMutex.Lock] be downgraded into a [RWMutex.RLock].//// In the terminology of [the Go memory model],// the n'th call to [RWMutex.Unlock] “synchronizes before” the m'th call to Lock// for any n < m, just as for [Mutex].// For any call to RLock, there exists an n such that// the n'th call to Unlock “synchronizes before” that call to RLock,// and the corresponding call to [RWMutex.RUnlock] “synchronizes before”// the n+1'th call to Lock.//// [the Go memory model]: https://go.dev/ref/memtype RWMutex struct {w Mutex // held if there are pending writerswriterSem uint32 // semaphore for writers to wait for completing readersreaderSem uint32 // semaphore for readers to wait for completing writersreaderCount atomic.Int32 // number of pending readersreaderWait atomic.Int32 // number of departing readers}const rwmutexMaxReaders = 1 << 30// Happens-before relationships are indicated to the race detector via:// - Unlock -> Lock: readerSem// - Unlock -> RLock: readerSem// - RUnlock -> Lock: writerSem//// The methods below temporarily disable handling of race synchronization// events in order to provide the more precise model above to the race// detector.//// For example, atomic.AddInt32 in RLock should not appear to provide// acquire-release semantics, which would incorrectly synchronize racing// readers, thus potentially missing races.// 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 ( *RWMutex) () {if race.Enabled {race.Read(unsafe.Pointer(&.w))race.Disable()}if .readerCount.Add(1) < 0 {// A writer is pending, wait for it.runtime_SemacquireRWMutexR(&.readerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&.readerSem))}}// TryRLock tries to lock rw for reading and reports whether it succeeded.//// Note that while correct uses of TryRLock do exist, they are rare,// and use of TryRLock is often a sign of a deeper problem// in a particular use of mutexes.func ( *RWMutex) () bool {if race.Enabled {race.Read(unsafe.Pointer(&.w))race.Disable()}for {:= .readerCount.Load()if < 0 {if race.Enabled {race.Enable()}return false}if .readerCount.CompareAndSwap(, +1) {if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&.readerSem))}return true}}}// RUnlock undoes a single [RWMutex.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 ( *RWMutex) () {if race.Enabled {race.Read(unsafe.Pointer(&.w))race.ReleaseMerge(unsafe.Pointer(&.writerSem))race.Disable()}if := .readerCount.Add(-1); < 0 {// Outlined slow-path to allow the fast-path to be inlined.rUnlockSlow()}if race.Enabled {race.Enable()}}func ( *RWMutex) ( int32) {if +1 == 0 || +1 == -rwmutexMaxReaders {race.Enable()fatal("sync: RUnlock of unlocked RWMutex")}// A writer is pending.if .readerWait.Add(-1) == 0 {// The last reader unblocks the writer.runtime_Semrelease(&.writerSem, false, 1)}}// Lock locks rw for writing.// If the lock is already locked for reading or writing,// Lock blocks until the lock is available.func ( *RWMutex) () {if race.Enabled {race.Read(unsafe.Pointer(&.w))race.Disable()}// First, resolve competition with other writers..w.Lock()// Announce to readers there is a pending writer.:= .readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders// Wait for active readers.if != 0 && .readerWait.Add() != 0 {runtime_SemacquireRWMutex(&.writerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&.readerSem))race.Acquire(unsafe.Pointer(&.writerSem))}}// TryLock tries to lock rw for writing and reports whether it succeeded.//// Note that while correct uses of TryLock do exist, they are rare,// and use of TryLock is often a sign of a deeper problem// in a particular use of mutexes.func ( *RWMutex) () bool {if race.Enabled {race.Read(unsafe.Pointer(&.w))race.Disable()}if !.w.TryLock() {if race.Enabled {race.Enable()}return false}if !.readerCount.CompareAndSwap(0, -rwmutexMaxReaders) {.w.Unlock()if race.Enabled {race.Enable()}return false}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&.readerSem))race.Acquire(unsafe.Pointer(&.writerSem))}return true}// 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 [RWMutex.RLock] ([RWMutex.Lock]) a RWMutex and then// arrange for another goroutine to [RWMutex.RUnlock] ([RWMutex.Unlock]) it.func ( *RWMutex) () {if race.Enabled {race.Read(unsafe.Pointer(&.w))race.Release(unsafe.Pointer(&.readerSem))race.Disable()}// Announce to readers there is no active writer.:= .readerCount.Add(rwmutexMaxReaders)if >= rwmutexMaxReaders {race.Enable()fatal("sync: Unlock of unlocked RWMutex")}// Unblock blocked readers, if any.for := 0; < int(); ++ {runtime_Semrelease(&.readerSem, false, 0)}// Allow other writers to proceed..w.Unlock()if race.Enabled {race.Enable()}}// syscall_hasWaitingReaders reports whether any goroutine is waiting// to acquire a read lock on rw. This exists because syscall.ForkLock// is an RWMutex, and we can't change that without breaking compatibility.// We don't need or want RWMutex semantics for ForkLock, and we use// this private API to avoid having to change the type of ForkLock.// For more details see the syscall package.////go:linkname syscall_hasWaitingReaders syscall.hasWaitingReadersfunc syscall_hasWaitingReaders( *RWMutex) bool {:= .readerCount.Load()return < 0 && +rwmutexMaxReaders > 0}// RLocker returns a [Locker] interface that implements// the [Locker.Lock] and [Locker.Unlock] methods by calling rw.RLock and rw.RUnlock.func ( *RWMutex) () Locker {return (*rlocker)()}type rlocker RWMutexfunc ( *rlocker) () { (*RWMutex)().RLock() }func ( *rlocker) () { (*RWMutex)().RUnlock() }
![]() |
The pages are generated with Golds v0.7.9-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |