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  // RLock locks rw for reading.
    39  //
    40  // It should not be used for recursive read locking; a blocked Lock
    41  // call excludes new readers from acquiring the lock. See the
    42  // documentation on the RWMutex type.
    43  func (rw *RWMutex) RLock() {
    44  	if race.Enabled {
    45  		_ = rw.w.state
    46  		race.Disable()
    47  	}
    48  	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
    49  		// A writer is pending, wait for it.
    50  		runtime_SemacquireMutex(&rw.readerSem, false, 0)
    51  	}
    52  	if race.Enabled {
    53  		race.Enable()
    54  		race.Acquire(unsafe.Pointer(&rw.readerSem))
    55  	}
    56  }
    57  
    58  // RUnlock undoes a single RLock call;
    59  // it does not affect other simultaneous readers.
    60  // It is a run-time error if rw is not locked for reading
    61  // on entry to RUnlock.
    62  func (rw *RWMutex) RUnlock() {
    63  	if race.Enabled {
    64  		_ = rw.w.state
    65  		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
    66  		race.Disable()
    67  	}
    68  	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
    69  		// Outlined slow-path to allow the fast-path to be inlined
    70  		rw.rUnlockSlow(r)
    71  	}
    72  	if race.Enabled {
    73  		race.Enable()
    74  	}
    75  }
    76  
    77  func (rw *RWMutex) rUnlockSlow(r int32) {
    78  	if r+1 == 0 || r+1 == -rwmutexMaxReaders {
    79  		race.Enable()
    80  		throw("sync: RUnlock of unlocked RWMutex")
    81  	}
    82  	// A writer is pending.
    83  	if atomic.AddInt32(&rw.readerWait, -1) == 0 {
    84  		// The last reader unblocks the writer.
    85  		runtime_Semrelease(&rw.writerSem, false, 1)
    86  	}
    87  }
    88  
    89  // Lock locks rw for writing.
    90  // If the lock is already locked for reading or writing,
    91  // Lock blocks until the lock is available.
    92  func (rw *RWMutex) Lock() {
    93  	if race.Enabled {
    94  		_ = rw.w.state
    95  		race.Disable()
    96  	}
    97  	// First, resolve competition with other writers.
    98  	rw.w.Lock()
    99  	// Announce to readers there is a pending writer.
   100  	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
   101  	// Wait for active readers.
   102  	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
   103  		runtime_SemacquireMutex(&rw.writerSem, false, 0)
   104  	}
   105  	if race.Enabled {
   106  		race.Enable()
   107  		race.Acquire(unsafe.Pointer(&rw.readerSem))
   108  		race.Acquire(unsafe.Pointer(&rw.writerSem))
   109  	}
   110  }
   111  
   112  // Unlock unlocks rw for writing. It is a run-time error if rw is
   113  // not locked for writing on entry to Unlock.
   114  //
   115  // As with Mutexes, a locked RWMutex is not associated with a particular
   116  // goroutine. One goroutine may RLock (Lock) a RWMutex and then
   117  // arrange for another goroutine to RUnlock (Unlock) it.
   118  func (rw *RWMutex) Unlock() {
   119  	if race.Enabled {
   120  		_ = rw.w.state
   121  		race.Release(unsafe.Pointer(&rw.readerSem))
   122  		race.Disable()
   123  	}
   124  
   125  	// Announce to readers there is no active writer.
   126  	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
   127  	if r >= rwmutexMaxReaders {
   128  		race.Enable()
   129  		throw("sync: Unlock of unlocked RWMutex")
   130  	}
   131  	// Unblock blocked readers, if any.
   132  	for i := 0; i < int(r); i++ {
   133  		runtime_Semrelease(&rw.readerSem, false, 0)
   134  	}
   135  	// Allow other writers to proceed.
   136  	rw.w.Unlock()
   137  	if race.Enabled {
   138  		race.Enable()
   139  	}
   140  }
   141  
   142  // RLocker returns a Locker interface that implements
   143  // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
   144  func (rw *RWMutex) RLocker() Locker {
   145  	return (*rlocker)(rw)
   146  }
   147  
   148  type rlocker RWMutex
   149  
   150  func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
   151  func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }
   152  

View as plain text