github.com/stingnevermore/go@v0.0.0-20180120041312-3810f5bfed72/src/runtime/rwmutex.go (about) 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 "runtime/internal/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 uint32 // number of pending readers 27 readerWait uint32 // number of departing readers 28 } 29 30 const rwmutexMaxReaders = 1 << 30 31 32 // rlock locks rw for reading. 33 func (rw *rwmutex) rlock() { 34 // The reader must not be allowed to lose its P or else other 35 // things blocking on the lock may consume all of the Ps and 36 // deadlock (issue #20903). Alternatively, we could drop the P 37 // while sleeping. 38 acquirem() 39 if int32(atomic.Xadd(&rw.readerCount, 1)) < 0 { 40 // A writer is pending. Park on the reader queue. 41 systemstack(func() { 42 lock(&rw.rLock) 43 if rw.readerPass > 0 { 44 // Writer finished. 45 rw.readerPass -= 1 46 unlock(&rw.rLock) 47 } else { 48 // Queue this reader to be woken by 49 // the writer. 50 m := getg().m 51 m.schedlink = rw.readers 52 rw.readers.set(m) 53 unlock(&rw.rLock) 54 notesleep(&m.park) 55 noteclear(&m.park) 56 } 57 }) 58 } 59 } 60 61 // runlock undoes a single rlock call on rw. 62 func (rw *rwmutex) runlock() { 63 if r := int32(atomic.Xadd(&rw.readerCount, -1)); r < 0 { 64 if r+1 == 0 || r+1 == -rwmutexMaxReaders { 65 throw("runlock of unlocked rwmutex") 66 } 67 // A writer is pending. 68 if atomic.Xadd(&rw.readerWait, -1) == 0 { 69 // The last reader unblocks the writer. 70 lock(&rw.rLock) 71 w := rw.writer.ptr() 72 if w != nil { 73 notewakeup(&w.park) 74 } 75 unlock(&rw.rLock) 76 } 77 } 78 releasem(getg().m) 79 } 80 81 // lock locks rw for writing. 82 func (rw *rwmutex) lock() { 83 // Resolve competition with other writers and stick to our P. 84 lock(&rw.wLock) 85 m := getg().m 86 // Announce that there is a pending writer. 87 r := int32(atomic.Xadd(&rw.readerCount, -rwmutexMaxReaders)) + rwmutexMaxReaders 88 // Wait for any active readers to complete. 89 lock(&rw.rLock) 90 if r != 0 && atomic.Xadd(&rw.readerWait, r) != 0 { 91 // Wait for reader to wake us up. 92 systemstack(func() { 93 rw.writer.set(m) 94 unlock(&rw.rLock) 95 notesleep(&m.park) 96 noteclear(&m.park) 97 }) 98 } else { 99 unlock(&rw.rLock) 100 } 101 } 102 103 // unlock unlocks rw for writing. 104 func (rw *rwmutex) unlock() { 105 // Announce to readers that there is no active writer. 106 r := int32(atomic.Xadd(&rw.readerCount, rwmutexMaxReaders)) 107 if r >= rwmutexMaxReaders { 108 throw("unlock of unlocked rwmutex") 109 } 110 // Unblock blocked readers. 111 lock(&rw.rLock) 112 for rw.readers.ptr() != nil { 113 reader := rw.readers.ptr() 114 rw.readers = reader.schedlink 115 reader.schedlink.set(nil) 116 notewakeup(&reader.park) 117 r -= 1 118 } 119 // If r > 0, there are pending readers that aren't on the 120 // queue. Tell them to skip waiting. 121 rw.readerPass += uint32(r) 122 unlock(&rw.rLock) 123 // Allow other writers to proceed. 124 unlock(&rw.wLock) 125 }