github.com/ActiveState/go@v0.0.0-20170614201249-0b81c023a722/src/sync/rwmutex.go (about)

     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  // An RWMutex is a reader/writer mutual exclusion lock.
    14  // The lock can be held by an arbitrary number of readers or a single writer.
    15  // The zero value for a RWMutex is an unlocked mutex.
    16  //
    17  // An RWMutex must not be copied after first use.
    18  //
    19  // If a goroutine holds a RWMutex for reading and another goroutine might
    20  // call Lock, no goroutine should expect to be able to acquire a read lock
    21  // until the initial read lock is released. In particular, this prohibits
    22  // recursive read locking. This is to ensure that the lock eventually becomes
    23  // available; a blocked Lock call excludes new readers from acquiring the
    24  // lock.
    25  type RWMutex struct {
    26  	w           Mutex  // held if there are pending writers
    27  	writerSem   uint32 // semaphore for writers to wait for completing readers
    28  	readerSem   uint32 // semaphore for readers to wait for completing writers
    29  	readerCount int32  // number of pending readers
    30  	readerWait  int32  // number of departing readers
    31  }
    32  
    33  const rwmutexMaxReaders = 1 << 30
    34  
    35  // RLock locks rw for reading.
    36  //
    37  // It should not be used for recursive read locking; a blocked Lock
    38  // call excludes new readers from acquiring the lock. See the
    39  // documentation on the RWMutex type.
    40  func (rw *RWMutex) RLock() {
    41  	if race.Enabled {
    42  		_ = rw.w.state
    43  		race.Disable()
    44  	}
    45  	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
    46  		// A writer is pending, wait for it.
    47  		runtime_Semacquire(&rw.readerSem)
    48  	}
    49  	if race.Enabled {
    50  		race.Enable()
    51  		race.Acquire(unsafe.Pointer(&rw.readerSem))
    52  	}
    53  }
    54  
    55  // RUnlock undoes a single RLock call;
    56  // it does not affect other simultaneous readers.
    57  // It is a run-time error if rw is not locked for reading
    58  // on entry to RUnlock.
    59  func (rw *RWMutex) RUnlock() {
    60  	if race.Enabled {
    61  		_ = rw.w.state
    62  		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
    63  		race.Disable()
    64  	}
    65  	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
    66  		if r+1 == 0 || r+1 == -rwmutexMaxReaders {
    67  			race.Enable()
    68  			throw("sync: RUnlock of unlocked RWMutex")
    69  		}
    70  		// A writer is pending.
    71  		if atomic.AddInt32(&rw.readerWait, -1) == 0 {
    72  			// The last reader unblocks the writer.
    73  			runtime_Semrelease(&rw.writerSem, false)
    74  		}
    75  	}
    76  	if race.Enabled {
    77  		race.Enable()
    78  	}
    79  }
    80  
    81  // Lock locks rw for writing.
    82  // If the lock is already locked for reading or writing,
    83  // Lock blocks until the lock is available.
    84  func (rw *RWMutex) Lock() {
    85  	if race.Enabled {
    86  		_ = rw.w.state
    87  		race.Disable()
    88  	}
    89  	// First, resolve competition with other writers.
    90  	rw.w.Lock()
    91  	// Announce to readers there is a pending writer.
    92  	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
    93  	// Wait for active readers.
    94  	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
    95  		runtime_Semacquire(&rw.writerSem)
    96  	}
    97  	if race.Enabled {
    98  		race.Enable()
    99  		race.Acquire(unsafe.Pointer(&rw.readerSem))
   100  		race.Acquire(unsafe.Pointer(&rw.writerSem))
   101  	}
   102  }
   103  
   104  // Unlock unlocks rw for writing. It is a run-time error if rw is
   105  // not locked for writing on entry to Unlock.
   106  //
   107  // As with Mutexes, a locked RWMutex is not associated with a particular
   108  // goroutine. One goroutine may RLock (Lock) an RWMutex and then
   109  // arrange for another goroutine to RUnlock (Unlock) it.
   110  func (rw *RWMutex) Unlock() {
   111  	if race.Enabled {
   112  		_ = rw.w.state
   113  		race.Release(unsafe.Pointer(&rw.readerSem))
   114  		race.Release(unsafe.Pointer(&rw.writerSem))
   115  		race.Disable()
   116  	}
   117  
   118  	// Announce to readers there is no active writer.
   119  	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
   120  	if r >= rwmutexMaxReaders {
   121  		race.Enable()
   122  		throw("sync: Unlock of unlocked RWMutex")
   123  	}
   124  	// Unblock blocked readers, if any.
   125  	for i := 0; i < int(r); i++ {
   126  		runtime_Semrelease(&rw.readerSem, false)
   127  	}
   128  	// Allow other writers to proceed.
   129  	rw.w.Unlock()
   130  	if race.Enabled {
   131  		race.Enable()
   132  	}
   133  }
   134  
   135  // RLocker returns a Locker interface that implements
   136  // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
   137  func (rw *RWMutex) RLocker() Locker {
   138  	return (*rlocker)(rw)
   139  }
   140  
   141  type rlocker RWMutex
   142  
   143  func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
   144  func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }