Source file src/runtime/rwmutex.go
1 // Copyright 2017 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 runtime 6 7 import ( 8 "internal/runtime/atomic" 9 ) 10 11 // This is a copy of sync/rwmutex.go rewritten to work in the runtime. 12 13 // A 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 // This is a variant of sync.RWMutex, for the runtime package. 16 // Like mutex, rwmutex blocks the calling M. 17 // It does not interact with the goroutine scheduler. 18 type rwmutex struct { 19 rLock mutex // protects readers, readerPass, writer 20 readers muintptr // list of pending readers 21 readerPass uint32 // number of pending readers to skip readers list 22 23 wLock mutex // serializes writers 24 writer muintptr // pending writer waiting for completing readers 25 26 readerCount atomic.Int32 // number of pending readers 27 readerWait atomic.Int32 // number of departing readers 28 29 readRank lockRank // semantic lock rank for read locking 30 } 31 32 // Lock ranking an rwmutex has two aspects: 33 // 34 // Semantic ranking: this rwmutex represents some higher level lock that 35 // protects some resource (e.g., allocmLock protects creation of new Ms). The 36 // read and write locks of that resource need to be represented in the lock 37 // rank. 38 // 39 // Internal ranking: as an implementation detail, rwmutex uses two mutexes: 40 // rLock and wLock. These have lock order requirements: wLock must be locked 41 // before rLock. This also needs to be represented in the lock rank. 42 // 43 // Semantic ranking is represented by acquiring readRank during read lock and 44 // writeRank during write lock. 45 // 46 // wLock is held for the duration of a write lock, so it uses writeRank 47 // directly, both for semantic and internal ranking. rLock is only held 48 // temporarily inside the rlock/lock methods, so it uses readRankInternal to 49 // represent internal ranking. Semantic ranking is represented by a separate 50 // acquire of readRank for the duration of a read lock. 51 // 52 // The lock ranking must document this ordering: 53 // - readRankInternal is a leaf lock. 54 // - readRank is taken before readRankInternal. 55 // - writeRank is taken before readRankInternal. 56 // - readRank is placed in the lock order wherever a read lock of this rwmutex 57 // belongs. 58 // - writeRank is placed in the lock order wherever a write lock of this 59 // rwmutex belongs. 60 func (rw *rwmutex) init(readRank, readRankInternal, writeRank lockRank) { 61 rw.readRank = readRank 62 63 lockInit(&rw.rLock, readRankInternal) 64 lockInit(&rw.wLock, writeRank) 65 } 66 67 const rwmutexMaxReaders = 1 << 30 68 69 // rlock locks rw for reading. 70 func (rw *rwmutex) rlock() { 71 // The reader must not be allowed to lose its P or else other 72 // things blocking on the lock may consume all of the Ps and 73 // deadlock (issue #20903). Alternatively, we could drop the P 74 // while sleeping. 75 acquireLockRankAndM(rw.readRank) 76 lockWithRankMayAcquire(&rw.rLock, getLockRank(&rw.rLock)) 77 78 if rw.readerCount.Add(1) < 0 { 79 // A writer is pending. Park on the reader queue. 80 systemstack(func() { 81 lock(&rw.rLock) 82 if rw.readerPass > 0 { 83 // Writer finished. 84 rw.readerPass -= 1 85 unlock(&rw.rLock) 86 } else { 87 // Queue this reader to be woken by 88 // the writer. 89 m := getg().m 90 m.schedlink = rw.readers 91 rw.readers.set(m) 92 unlock(&rw.rLock) 93 notesleep(&m.park) 94 noteclear(&m.park) 95 } 96 }) 97 } 98 } 99 100 // runlock undoes a single rlock call on rw. 101 func (rw *rwmutex) runlock() { 102 if r := rw.readerCount.Add(-1); r < 0 { 103 if r+1 == 0 || r+1 == -rwmutexMaxReaders { 104 throw("runlock of unlocked rwmutex") 105 } 106 // A writer is pending. 107 if rw.readerWait.Add(-1) == 0 { 108 // The last reader unblocks the writer. 109 lock(&rw.rLock) 110 w := rw.writer.ptr() 111 if w != nil { 112 notewakeup(&w.park) 113 } 114 unlock(&rw.rLock) 115 } 116 } 117 releaseLockRankAndM(rw.readRank) 118 } 119 120 // lock locks rw for writing. 121 func (rw *rwmutex) lock() { 122 // Resolve competition with other writers and stick to our P. 123 lock(&rw.wLock) 124 m := getg().m 125 // Announce that there is a pending writer. 126 r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders 127 // Wait for any active readers to complete. 128 lock(&rw.rLock) 129 if r != 0 && rw.readerWait.Add(r) != 0 { 130 // Wait for reader to wake us up. 131 systemstack(func() { 132 rw.writer.set(m) 133 unlock(&rw.rLock) 134 notesleep(&m.park) 135 noteclear(&m.park) 136 }) 137 } else { 138 unlock(&rw.rLock) 139 } 140 } 141 142 // unlock unlocks rw for writing. 143 func (rw *rwmutex) unlock() { 144 // Announce to readers that there is no active writer. 145 r := rw.readerCount.Add(rwmutexMaxReaders) 146 if r >= rwmutexMaxReaders { 147 throw("unlock of unlocked rwmutex") 148 } 149 // Unblock blocked readers. 150 lock(&rw.rLock) 151 for rw.readers.ptr() != nil { 152 reader := rw.readers.ptr() 153 rw.readers = reader.schedlink 154 reader.schedlink.set(nil) 155 notewakeup(&reader.park) 156 r -= 1 157 } 158 // If r > 0, there are pending readers that aren't on the 159 // queue. Tell them to skip waiting. 160 rw.readerPass += uint32(r) 161 unlock(&rw.rLock) 162 // Allow other writers to proceed. 163 unlock(&rw.wLock) 164 } 165