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 	"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() }
    137