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 // 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)
     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)
    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() }
    141