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

View as plain text