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