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

View as plain text