Home | History | Annotate | Download | only in sync
      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() }
    148