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