Source file src/sync/rwmutex.go

     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 any goroutine calls [RWMutex.Lock] while the lock is already held by
    23  // one or more readers, concurrent calls to [RWMutex.RLock] will block until
    24  // the writer has acquired (and released) the lock, to ensure that
    25  // the lock eventually becomes available to the writer.
    26  // Note that this prohibits recursive read-locking.
    27  // A [RWMutex.RLock] cannot be upgraded into a [RWMutex.Lock],
    28  // nor can a [RWMutex.Lock] be downgraded into a [RWMutex.RLock].
    29  //
    30  // In the terminology of [the Go memory model],
    31  // the n'th call to [RWMutex.Unlock] “synchronizes before” the m'th call to Lock
    32  // for any n < m, just as for [Mutex].
    33  // For any call to RLock, there exists an n such that
    34  // the n'th call to Unlock “synchronizes before” that call to RLock,
    35  // and the corresponding call to [RWMutex.RUnlock] “synchronizes before”
    36  // the n+1'th call to Lock.
    37  //
    38  // [the Go memory model]: https://go.dev/ref/mem
    39  type RWMutex struct {
    40  	w           Mutex        // held if there are pending writers
    41  	writerSem   uint32       // semaphore for writers to wait for completing readers
    42  	readerSem   uint32       // semaphore for readers to wait for completing writers
    43  	readerCount atomic.Int32 // number of pending readers
    44  	readerWait  atomic.Int32 // number of departing readers
    45  }
    46  
    47  const rwmutexMaxReaders = 1 << 30
    48  
    49  // Happens-before relationships are indicated to the race detector via:
    50  // - Unlock  -> Lock:  readerSem
    51  // - Unlock  -> RLock: readerSem
    52  // - RUnlock -> Lock:  writerSem
    53  //
    54  // The methods below temporarily disable handling of race synchronization
    55  // events in order to provide the more precise model above to the race
    56  // detector.
    57  //
    58  // For example, atomic.AddInt32 in RLock should not appear to provide
    59  // acquire-release semantics, which would incorrectly synchronize racing
    60  // readers, thus potentially missing races.
    61  
    62  // RLock locks rw for reading.
    63  //
    64  // It should not be used for recursive read locking; a blocked Lock
    65  // call excludes new readers from acquiring the lock. See the
    66  // documentation on the [RWMutex] type.
    67  func (rw *RWMutex) RLock() {
    68  	if race.Enabled {
    69  		race.Read(unsafe.Pointer(&rw.w))
    70  		race.Disable()
    71  	}
    72  	if rw.readerCount.Add(1) < 0 {
    73  		// A writer is pending, wait for it.
    74  		runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)
    75  	}
    76  	if race.Enabled {
    77  		race.Enable()
    78  		race.Acquire(unsafe.Pointer(&rw.readerSem))
    79  	}
    80  }
    81  
    82  // TryRLock tries to lock rw for reading and reports whether it succeeded.
    83  //
    84  // Note that while correct uses of TryRLock do exist, they are rare,
    85  // and use of TryRLock is often a sign of a deeper problem
    86  // in a particular use of mutexes.
    87  func (rw *RWMutex) TryRLock() bool {
    88  	if race.Enabled {
    89  		race.Read(unsafe.Pointer(&rw.w))
    90  		race.Disable()
    91  	}
    92  	for {
    93  		c := rw.readerCount.Load()
    94  		if c < 0 {
    95  			if race.Enabled {
    96  				race.Enable()
    97  			}
    98  			return false
    99  		}
   100  		if rw.readerCount.CompareAndSwap(c, c+1) {
   101  			if race.Enabled {
   102  				race.Enable()
   103  				race.Acquire(unsafe.Pointer(&rw.readerSem))
   104  			}
   105  			return true
   106  		}
   107  	}
   108  }
   109  
   110  // RUnlock undoes a single [RWMutex.RLock] call;
   111  // it does not affect other simultaneous readers.
   112  // It is a run-time error if rw is not locked for reading
   113  // on entry to RUnlock.
   114  func (rw *RWMutex) RUnlock() {
   115  	if race.Enabled {
   116  		race.Read(unsafe.Pointer(&rw.w))
   117  		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
   118  		race.Disable()
   119  	}
   120  	if r := rw.readerCount.Add(-1); r < 0 {
   121  		// Outlined slow-path to allow the fast-path to be inlined
   122  		rw.rUnlockSlow(r)
   123  	}
   124  	if race.Enabled {
   125  		race.Enable()
   126  	}
   127  }
   128  
   129  func (rw *RWMutex) rUnlockSlow(r int32) {
   130  	if r+1 == 0 || r+1 == -rwmutexMaxReaders {
   131  		race.Enable()
   132  		fatal("sync: RUnlock of unlocked RWMutex")
   133  	}
   134  	// A writer is pending.
   135  	if rw.readerWait.Add(-1) == 0 {
   136  		// The last reader unblocks the writer.
   137  		runtime_Semrelease(&rw.writerSem, false, 1)
   138  	}
   139  }
   140  
   141  // Lock locks rw for writing.
   142  // If the lock is already locked for reading or writing,
   143  // Lock blocks until the lock is available.
   144  func (rw *RWMutex) Lock() {
   145  	if race.Enabled {
   146  		race.Read(unsafe.Pointer(&rw.w))
   147  		race.Disable()
   148  	}
   149  	// First, resolve competition with other writers.
   150  	rw.w.Lock()
   151  	// Announce to readers there is a pending writer.
   152  	r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders
   153  	// Wait for active readers.
   154  	if r != 0 && rw.readerWait.Add(r) != 0 {
   155  		runtime_SemacquireRWMutex(&rw.writerSem, false, 0)
   156  	}
   157  	if race.Enabled {
   158  		race.Enable()
   159  		race.Acquire(unsafe.Pointer(&rw.readerSem))
   160  		race.Acquire(unsafe.Pointer(&rw.writerSem))
   161  	}
   162  }
   163  
   164  // TryLock tries to lock rw for writing and reports whether it succeeded.
   165  //
   166  // Note that while correct uses of TryLock do exist, they are rare,
   167  // and use of TryLock is often a sign of a deeper problem
   168  // in a particular use of mutexes.
   169  func (rw *RWMutex) TryLock() bool {
   170  	if race.Enabled {
   171  		race.Read(unsafe.Pointer(&rw.w))
   172  		race.Disable()
   173  	}
   174  	if !rw.w.TryLock() {
   175  		if race.Enabled {
   176  			race.Enable()
   177  		}
   178  		return false
   179  	}
   180  	if !rw.readerCount.CompareAndSwap(0, -rwmutexMaxReaders) {
   181  		rw.w.Unlock()
   182  		if race.Enabled {
   183  			race.Enable()
   184  		}
   185  		return false
   186  	}
   187  	if race.Enabled {
   188  		race.Enable()
   189  		race.Acquire(unsafe.Pointer(&rw.readerSem))
   190  		race.Acquire(unsafe.Pointer(&rw.writerSem))
   191  	}
   192  	return true
   193  }
   194  
   195  // Unlock unlocks rw for writing. It is a run-time error if rw is
   196  // not locked for writing on entry to Unlock.
   197  //
   198  // As with Mutexes, a locked [RWMutex] is not associated with a particular
   199  // goroutine. One goroutine may [RWMutex.RLock] ([RWMutex.Lock]) a RWMutex and then
   200  // arrange for another goroutine to [RWMutex.RUnlock] ([RWMutex.Unlock]) it.
   201  func (rw *RWMutex) Unlock() {
   202  	if race.Enabled {
   203  		race.Read(unsafe.Pointer(&rw.w))
   204  		race.Release(unsafe.Pointer(&rw.readerSem))
   205  		race.Disable()
   206  	}
   207  
   208  	// Announce to readers there is no active writer.
   209  	r := rw.readerCount.Add(rwmutexMaxReaders)
   210  	if r >= rwmutexMaxReaders {
   211  		race.Enable()
   212  		fatal("sync: Unlock of unlocked RWMutex")
   213  	}
   214  	// Unblock blocked readers, if any.
   215  	for i := 0; i < int(r); i++ {
   216  		runtime_Semrelease(&rw.readerSem, false, 0)
   217  	}
   218  	// Allow other writers to proceed.
   219  	rw.w.Unlock()
   220  	if race.Enabled {
   221  		race.Enable()
   222  	}
   223  }
   224  
   225  // syscall_hasWaitingReaders reports whether any goroutine is waiting
   226  // to acquire a read lock on rw. This exists because syscall.ForkLock
   227  // is an RWMutex, and we can't change that without breaking compatibility.
   228  // We don't need or want RWMutex semantics for ForkLock, and we use
   229  // this private API to avoid having to change the type of ForkLock.
   230  // For more details see the syscall package.
   231  //
   232  //go:linkname syscall_hasWaitingReaders syscall.hasWaitingReaders
   233  func syscall_hasWaitingReaders(rw *RWMutex) bool {
   234  	r := rw.readerCount.Load()
   235  	return r < 0 && r+rwmutexMaxReaders > 0
   236  }
   237  
   238  // RLocker returns a [Locker] interface that implements
   239  // the [Locker.Lock] and [Locker.Unlock] methods by calling rw.RLock and rw.RUnlock.
   240  func (rw *RWMutex) RLocker() Locker {
   241  	return (*rlocker)(rw)
   242  }
   243  
   244  type rlocker RWMutex
   245  
   246  func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
   247  func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }
   248  

View as plain text