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