github.com/gitbookio/syncgroup@v0.0.0-20181003125046-3e73b2e6a972/mutex/rwmutex.go (about)

     1  // Originally copied from https://golang.org/src/sync/rwmutex.go on Jan 5th 2017
     2  //
     3  // Key changes:
     4  // - strip "race" checks
     5  // - return number of active holders
     6  
     7  // Copyright 2009 The Go Authors. All rights reserved.
     8  // Use of this source code is governed by a BSD-style
     9  // license that can be found in the LICENSE file.
    10  
    11  package mutex
    12  
    13  import (
    14  	"sync/atomic"
    15  	"unsafe"
    16  )
    17  
    18  // An RWMutex is a reader/writer mutual exclusion lock.
    19  // The lock can be held by an arbitrary number of readers or a single writer.
    20  // RWMutexes can be created as part of other structures;
    21  // the zero value for a RWMutex is an unlocked mutex.
    22  //
    23  // An RWMutex must not be copied after first use.
    24  //
    25  // If a goroutine holds a RWMutex for reading, it must not expect this or any
    26  // other goroutine to be able to also take the read lock until the first read
    27  // lock is released. In particular, this prohibits recursive read locking.
    28  // This is to ensure that the lock eventually becomes available;
    29  // a blocked Lock call excludes new readers from acquiring the lock.
    30  type RWMutex struct {
    31  	w           Mutex  // held if there are pending writers
    32  	writerSem   uint32 // semaphore for writers to wait for completing readers
    33  	readerSem   uint32 // semaphore for readers to wait for completing writers
    34  	readerCount int32  // number of pending readers
    35  	readerWait  int32  // number of departing readers
    36  }
    37  
    38  const rwmutexMaxReaders = 1 << 30
    39  
    40  // RLock locks rw for reading.
    41  func (rw *RWMutex) RLock() {
    42  	if race_Enabled {
    43  		_ = rw.w.state
    44  		race_Disable()
    45  	}
    46  	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
    47  		// A writer is pending, wait for it.
    48  		runtime_Semacquire(&rw.readerSem)
    49  	}
    50  	if race_Enabled {
    51  		race_Enable()
    52  		race_Acquire(unsafe.Pointer(&rw.readerSem))
    53  	}
    54  }
    55  
    56  // RUnlock undoes a single RLock call;
    57  // it does not affect other simultaneous readers.
    58  // It is a run-time error if rw is not locked for reading
    59  // on entry to RUnlock.
    60  func (rw *RWMutex) RUnlock() {
    61  	if race_Enabled {
    62  		_ = rw.w.state
    63  		race_ReleaseMerge(unsafe.Pointer(&rw.writerSem))
    64  		race_Disable()
    65  	}
    66  	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
    67  		if r+1 == 0 || r+1 == -rwmutexMaxReaders {
    68  			race_Enable()
    69  			panic("sync: RUnlock of unlocked RWMutex")
    70  		}
    71  		// A writer is pending.
    72  		if atomic.AddInt32(&rw.readerWait, -1) == 0 {
    73  			// The last reader unblocks the writer.
    74  			runtime_Semrelease(&rw.writerSem)
    75  		}
    76  	}
    77  	if race_Enabled {
    78  		race_Enable()
    79  	}
    80  }
    81  
    82  // Lock locks rw for writing.
    83  // If the lock is already locked for reading or writing,
    84  // Lock blocks until the lock is available.
    85  func (rw *RWMutex) Lock() {
    86  	if race_Enabled {
    87  		_ = rw.w.state
    88  		race_Disable()
    89  	}
    90  	// First, resolve competition with other writers.
    91  	rw.w.Lock()
    92  	// Announce to readers there is a pending writer.
    93  	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
    94  	// Wait for active readers.
    95  	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
    96  		runtime_Semacquire(&rw.writerSem)
    97  	}
    98  	if race_Enabled {
    99  		race_Enable()
   100  		race_Acquire(unsafe.Pointer(&rw.readerSem))
   101  		race_Acquire(unsafe.Pointer(&rw.writerSem))
   102  	}
   103  }
   104  
   105  // Unlock unlocks rw for writing. It is a run-time error if rw is
   106  // not locked for writing on entry to Unlock.
   107  //
   108  // As with Mutexes, a locked RWMutex is not associated with a particular
   109  // goroutine. One goroutine may RLock (Lock) an RWMutex and then
   110  // arrange for another goroutine to RUnlock (Unlock) it.
   111  func (rw *RWMutex) Unlock() {
   112  	if race_Enabled {
   113  		_ = rw.w.state
   114  		race_Release(unsafe.Pointer(&rw.readerSem))
   115  		race_Release(unsafe.Pointer(&rw.writerSem))
   116  		race_Disable()
   117  	}
   118  
   119  	// Announce to readers there is no active writer.
   120  	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
   121  	if r >= rwmutexMaxReaders {
   122  		race_Enable()
   123  		panic("sync: Unlock of unlocked RWMutex")
   124  	}
   125  	// Unblock blocked readers, if any.
   126  	for i := 0; i < int(r); i++ {
   127  		runtime_Semrelease(&rw.readerSem)
   128  	}
   129  	// Allow other writers to proceed.
   130  	rw.w.Unlock()
   131  	if race_Enabled {
   132  		race_Enable()
   133  	}
   134  }
   135  
   136  // RLocker returns a Locker interface that implements
   137  // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
   138  func (rw *RWMutex) RLocker() Locker {
   139  	return (*rlocker)(rw)
   140  }
   141  
   142  type rlocker RWMutex
   143  
   144  func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
   145  func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }