Black Lives Matter. Support the Equal Justice Initiative.

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 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  // RUnlock undoes a single RLock call;
    72  // it does not affect other simultaneous readers.
    73  // It is a run-time error if rw is not locked for reading
    74  // on entry to RUnlock.
    75  func (rw *RWMutex) RUnlock() {
    76  	if race.Enabled {
    77  		_ = rw.w.state
    78  		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
    79  		race.Disable()
    80  	}
    81  	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
    82  		// Outlined slow-path to allow the fast-path to be inlined
    83  		rw.rUnlockSlow(r)
    84  	}
    85  	if race.Enabled {
    86  		race.Enable()
    87  	}
    88  }
    89  
    90  func (rw *RWMutex) rUnlockSlow(r int32) {
    91  	if r+1 == 0 || r+1 == -rwmutexMaxReaders {
    92  		race.Enable()
    93  		throw("sync: RUnlock of unlocked RWMutex")
    94  	}
    95  	// A writer is pending.
    96  	if atomic.AddInt32(&rw.readerWait, -1) == 0 {
    97  		// The last reader unblocks the writer.
    98  		runtime_Semrelease(&rw.writerSem, false, 1)
    99  	}
   100  }
   101  
   102  // Lock locks rw for writing.
   103  // If the lock is already locked for reading or writing,
   104  // Lock blocks until the lock is available.
   105  func (rw *RWMutex) Lock() {
   106  	if race.Enabled {
   107  		_ = rw.w.state
   108  		race.Disable()
   109  	}
   110  	// First, resolve competition with other writers.
   111  	rw.w.Lock()
   112  	// Announce to readers there is a pending writer.
   113  	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
   114  	// Wait for active readers.
   115  	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
   116  		runtime_SemacquireMutex(&rw.writerSem, false, 0)
   117  	}
   118  	if race.Enabled {
   119  		race.Enable()
   120  		race.Acquire(unsafe.Pointer(&rw.readerSem))
   121  		race.Acquire(unsafe.Pointer(&rw.writerSem))
   122  	}
   123  }
   124  
   125  // Unlock unlocks rw for writing. It is a run-time error if rw is
   126  // not locked for writing on entry to Unlock.
   127  //
   128  // As with Mutexes, a locked RWMutex is not associated with a particular
   129  // goroutine. One goroutine may RLock (Lock) a RWMutex and then
   130  // arrange for another goroutine to RUnlock (Unlock) it.
   131  func (rw *RWMutex) Unlock() {
   132  	if race.Enabled {
   133  		_ = rw.w.state
   134  		race.Release(unsafe.Pointer(&rw.readerSem))
   135  		race.Disable()
   136  	}
   137  
   138  	// Announce to readers there is no active writer.
   139  	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
   140  	if r >= rwmutexMaxReaders {
   141  		race.Enable()
   142  		throw("sync: Unlock of unlocked RWMutex")
   143  	}
   144  	// Unblock blocked readers, if any.
   145  	for i := 0; i < int(r); i++ {
   146  		runtime_Semrelease(&rw.readerSem, false, 0)
   147  	}
   148  	// Allow other writers to proceed.
   149  	rw.w.Unlock()
   150  	if race.Enabled {
   151  		race.Enable()
   152  	}
   153  }
   154  
   155  // RLocker returns a Locker interface that implements
   156  // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
   157  func (rw *RWMutex) RLocker() Locker {
   158  	return (*rlocker)(rw)
   159  }
   160  
   161  type rlocker RWMutex
   162  
   163  func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
   164  func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }
   165  

View as plain text