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

View as plain text