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