mutex.mx raw

   1  package sync
   2  
   3  import (
   4  	"internal/task"
   5  )
   6  
   7  type Mutex = task.Mutex
   8  
   9  //go:linkname runtimePanic runtime.runtimePanic
  10  func runtimePanic(msg []byte)
  11  
  12  type RWMutex struct {
  13  	// Reader count, with the number of readers that currently have read-locked
  14  	// this mutex.
  15  	// The value can be in two states: one where 0 means no readers and another
  16  	// where -rwMutexMaxReaders means no readers. A base of 0 is normal
  17  	// uncontended operation, a base of -rwMutexMaxReaders means a writer has
  18  	// the lock or is trying to get the lock. In the second case, readers should
  19  	// wait until the reader count becomes non-negative again to give the writer
  20  	// a chance to obtain the lock.
  21  	readers task.Futex
  22  
  23  	// Writer futex, normally 0. If there is a writer waiting until all readers
  24  	// have unlocked, this value is 1. It will be changed to a 2 (and get a
  25  	// wake) when the last reader unlocks.
  26  	writer task.Futex
  27  
  28  	// Writer lock. Held between Lock() and Unlock().
  29  	writerLock Mutex
  30  }
  31  
  32  const rwMutexMaxReaders = 1 << 30
  33  
  34  // Lock locks rw for writing.
  35  // If the lock is already locked for reading or writing,
  36  // Lock blocks until the lock is available.
  37  func (rw *RWMutex) Lock() {
  38  	// Exclusive lock for writers.
  39  	rw.writerLock.Lock()
  40  
  41  	// Flag that we need to be awakened after the last read-lock unlocks.
  42  	rw.writer.Store(1)
  43  
  44  	// Signal to readers that they can't lock this mutex anymore.
  45  	n := uint32(rwMutexMaxReaders)
  46  	waiting := rw.readers.Add(-n)
  47  	if int32(waiting) == -rwMutexMaxReaders {
  48  		// All readers were already unlocked, so we don't need to wait for them.
  49  		rw.writer.Store(0)
  50  		return
  51  	}
  52  
  53  	// There is at least one reader.
  54  	// Wait until all readers are unlocked. The last reader to unlock will set
  55  	// rw.writer to 2 and awaken us.
  56  	for rw.writer.Load() == 1 {
  57  		rw.writer.Wait(1)
  58  	}
  59  	rw.writer.Store(0)
  60  }
  61  
  62  // Unlock unlocks rw for writing. It is a run-time error if rw is
  63  // not locked for writing on entry to Unlock.
  64  //
  65  // As with Mutexes, a locked [RWMutex] is not associated with a particular
  66  // goroutine. One goroutine may [RWMutex.RLock] ([RWMutex.Lock]) a RWMutex and then
  67  // arrange for another goroutine to [RWMutex.RUnlock] ([RWMutex.Unlock]) it.
  68  func (rw *RWMutex) Unlock() {
  69  	// Signal that new readers can lock this mutex.
  70  	waiting := rw.readers.Add(rwMutexMaxReaders)
  71  	if waiting != 0 {
  72  		// Awaken all waiting readers.
  73  		rw.readers.WakeAll()
  74  	}
  75  
  76  	// Done with this lock (next writer can try to get a lock).
  77  	rw.writerLock.Unlock()
  78  }
  79  
  80  // TryLock tries to lock m and reports whether it succeeded.
  81  //
  82  // Note that while correct uses of TryLock do exist, they are rare,
  83  // and use of TryLock is often a sign of a deeper problem
  84  // in a particular use of mutexes.
  85  func (rw *RWMutex) TryLock() bool {
  86  	// Check for active writers
  87  	if !rw.writerLock.TryLock() {
  88  		return false
  89  	}
  90  	// Have write lock, now check for active readers
  91  	n := uint32(rwMutexMaxReaders)
  92  	if !rw.readers.CompareAndSwap(0, -n) {
  93  		// Active readers, give up write lock
  94  		rw.writerLock.Unlock()
  95  		return false
  96  	}
  97  	return true
  98  }
  99  
 100  // RLock locks rw for reading.
 101  //
 102  // It should not be used for recursive read locking; a blocked Lock
 103  // call excludes new readers from acquiring the lock. See the
 104  // documentation on the [RWMutex] type.
 105  func (rw *RWMutex) RLock() {
 106  	// Add us as a reader.
 107  	newVal := rw.readers.Add(1)
 108  
 109  	// Wait until the RWMutex is available for readers.
 110  	for int32(newVal) <= 0 {
 111  		rw.readers.Wait(newVal)
 112  		newVal = rw.readers.Load()
 113  	}
 114  }
 115  
 116  // RUnlock undoes a single [RWMutex.RLock] call;
 117  // it does not affect other simultaneous readers.
 118  // It is a run-time error if rw is not locked for reading
 119  // on entry to RUnlock.
 120  func (rw *RWMutex) RUnlock() {
 121  	// Remove us as a reader.
 122  	one := uint32(1)
 123  	readers := int32(rw.readers.Add(-one))
 124  
 125  	// Check whether RUnlock was called too often.
 126  	if readers == -1 || readers == (-rwMutexMaxReaders)-1 {
 127  		runtimePanic("sync: RUnlock of unlocked RWMutex")
 128  	}
 129  
 130  	if readers == -rwMutexMaxReaders {
 131  		// This was the last read lock. Check whether we need to wake up a write
 132  		// lock.
 133  		if rw.writer.CompareAndSwap(1, 2) {
 134  			rw.writer.Wake()
 135  		}
 136  	}
 137  }
 138  
 139  // TryRLock tries to lock rw for reading and reports whether it succeeded.
 140  //
 141  // Note that while correct uses of TryRLock do exist, they are rare,
 142  // and use of TryRLock is often a sign of a deeper problem
 143  // in a particular use of mutexes.
 144  func (rw *RWMutex) TryRLock() bool {
 145  	for {
 146  		c := rw.readers.Load()
 147  		if c < 0 {
 148  			// There is a writer waiting or writing.
 149  			return false
 150  		}
 151  		if rw.readers.CompareAndSwap(c, c+1) {
 152  			// Read lock obtained.
 153  			return true
 154  		}
 155  	}
 156  }
 157  
 158  type Locker interface {
 159  	Lock()
 160  	Unlock()
 161  }
 162  
 163  // RLocker returns a Locker interface that implements
 164  // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
 165  func (rw *RWMutex) RLocker() Locker {
 166  	return (*rlocker)(rw)
 167  }
 168  
 169  type rlocker RWMutex
 170  
 171  func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
 172  func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }
 173