...

Source file src/sync/rwmutex.go

Documentation: sync

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

View as plain text