github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/go-weave/models/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 // +build ignore 6 7 // rwmutex is a model of runtime.rwmutex. 8 package main 9 10 import ( 11 "github.com/aclements/go-misc/go-weave/amb" 12 "github.com/aclements/go-misc/go-weave/weave" 13 ) 14 15 //var sched = weave.Scheduler{Strategy: &amb.StrategyRandom{}} 16 var sched = weave.Scheduler{Strategy: &amb.StrategyDFS{}} 17 18 const verbose = false 19 20 func main() { 21 sched.Run(func() { 22 if verbose { 23 print("start:") 24 } 25 var rw rwmutex 26 for i := 0; i < 2; i++ { 27 sched.Go(func() { 28 rw.lock() 29 rw.unlock() 30 }) 31 } 32 for i := 0; i < 2; i++ { 33 sched.Go(func() { 34 rw.rlock() 35 rw.runlock() 36 }) 37 } 38 }) 39 } 40 41 func atomicXadd(x *uint32, delta int32) uint32 { 42 *x += uint32(delta) 43 r := *x 44 sched.Sched() 45 return r 46 } 47 48 func atomicLoad(x *uint32) uint32 { 49 r := *x 50 sched.Sched() 51 return r 52 } 53 54 func lock(m *weave.Mutex) { 55 m.Lock() 56 } 57 58 func unlock(m *weave.Mutex) { 59 m.Unlock() 60 } 61 62 type m struct { 63 schedlink muintptr 64 park weave.Semaphore 65 } 66 67 type g struct { 68 m *m 69 } 70 71 var curG = weave.NewTLS() 72 73 func notesleep(s *weave.Semaphore) { 74 s.Acquire(1) 75 } 76 77 func notewakeup(s *weave.Semaphore) { 78 s.Release(1) 79 } 80 81 func noteclear(s *weave.Semaphore) { 82 } 83 84 func getg() *g { 85 gp, ok := curG.Get().(*g) 86 if !ok { 87 gp = &g{&m{}} 88 curG.Set(gp) 89 } 90 return gp 91 } 92 93 type rwmutex struct { 94 rLock weave.Mutex // protects readers, readerPass, writer 95 readers muintptr // list of pending readers 96 readerPass uint32 // number of readers to skip readers list 97 98 wLock weave.Mutex // serializes writers 99 writer muintptr // pending writer waiting for completing readers 100 101 readerCount uint32 // number of pending readers 102 readerWait uint32 // number of departing readers 103 104 // Self-checking 105 checkReaders uint32 106 checkWriters uint32 107 } 108 109 type muintptr struct { 110 mp *m 111 } 112 113 func (mp *muintptr) set(x *m) { 114 mp.mp = x 115 } 116 117 func (mp *muintptr) ptr() *m { 118 return mp.mp 119 } 120 121 func systemstack(x func()) { 122 x() 123 } 124 125 func throw(x string) { 126 panic(x) 127 } 128 129 const rwmutexMaxReaders = 1 << 30 130 131 // rlock locks rw for reading. 132 func (rw *rwmutex) rlock() { 133 sched.Tracef("rw.readerCount (%d) += 1", rw.readerCount) 134 if int32(atomicXadd(&rw.readerCount, 1)) < 0 { 135 // A writer is pending. Park on the reader queue. 136 sched.Trace("writer pending") 137 systemstack(func() { 138 lock(&rw.rLock) 139 // Writer may have released while we were 140 // getting the lock. 141 sched.Trace("got rLock") 142 if rw.readerPass > 0 { 143 // Writer finished. 144 rw.readerPass -= 1 145 unlock(&rw.rLock) 146 } else { 147 // Queue this reader to be woken by 148 // the writer. 149 m := getg().m 150 m.schedlink = rw.readers 151 rw.readers.set(m) 152 sched.Trace("reader queued") 153 unlock(&rw.rLock) 154 notesleep(&m.park) 155 noteclear(&m.park) 156 } 157 }) 158 } 159 160 // Self-check 161 if rw.checkWriters != 0 { 162 panic("rlock with writers") 163 } 164 rw.checkReaders++ 165 } 166 167 // runlock undoes a single rlock call on rw. 168 func (rw *rwmutex) runlock() { 169 if rw.checkReaders <= 0 { 170 panic("runlock with no readers") 171 } 172 if rw.checkWriters != 0 { 173 panic("runlock with writers") 174 } 175 rw.checkReaders-- 176 177 sched.Tracef("rw.readerCount (%d) -= 1", rw.readerCount) 178 if r := int32(atomicXadd(&rw.readerCount, -1)); r < 0 { 179 sched.Tracef("r = %d", r) 180 if r+1 == 0 || r+1 == -rwmutexMaxReaders { 181 throw("runlock of unlocked rwmutex") 182 } 183 // A writer is pending. 184 sched.Tracef("rw.readerWait (%d) -= 1", rw.readerWait) 185 if atomicXadd(&rw.readerWait, -1) == 0 { 186 // The last reader unblocks the writer. 187 sched.Trace("last reader") 188 lock(&rw.rLock) 189 w := rw.writer.ptr() 190 if w != nil { 191 sched.Trace("wake writer") 192 notewakeup(&w.park) 193 } 194 unlock(&rw.rLock) 195 } 196 } 197 } 198 199 // lock locks rw for writing. 200 func (rw *rwmutex) lock() { 201 // Resolve competition with other writers. 202 lock(&rw.wLock) 203 sched.Trace("got wLock") 204 m := getg().m 205 // Announce that there is a pending writer. 206 sched.Tracef("rw.readerCount (%d) -= rwmutexMaxReaders", rw.readerCount) 207 r := int32(atomicXadd(&rw.readerCount, -rwmutexMaxReaders)) + rwmutexMaxReaders 208 // Wait for any active readers to complete. 209 lock(&rw.rLock) // NEW 210 if r != 0 { 211 sched.Tracef("rw.readerWait (%d) += %d", rw.readerWait, r) 212 } 213 if r != 0 && atomicXadd(&rw.readerWait, r) != 0 { 214 sched.Trace("waiting for readers") 215 // Wait for reader to wake us up. 216 systemstack(func() { 217 rw.writer.set(m) 218 unlock(&rw.rLock) // NEW 219 notesleep(&m.park) 220 noteclear(&m.park) 221 }) 222 } else { 223 sched.Trace("no readers") 224 unlock(&rw.rLock) // NEW 225 } 226 227 // Self-check 228 if rw.checkReaders != 0 { 229 panic("lock with readers") 230 } 231 if rw.checkWriters != 0 { 232 panic("lock with writers") 233 } 234 rw.checkWriters++ 235 } 236 237 // unlock unlocks rw for writing. 238 func (rw *rwmutex) unlock() { 239 // Self-check 240 if rw.checkReaders != 0 { 241 panic("unlock with readers") 242 } 243 if rw.checkWriters != 1 { 244 panic("unlock with wrong writers") 245 } 246 rw.checkWriters-- 247 248 // Announce to readers that there is no active writer. 249 sched.Tracef("rw.readerCount (%d) += rwmutexMaxReaders", rw.readerCount) 250 r := int32(atomicXadd(&rw.readerCount, rwmutexMaxReaders)) 251 if r >= rwmutexMaxReaders { 252 throw("unlock of unlocked rwmutex") 253 } 254 // Unblock blocked readers. 255 lock(&rw.rLock) 256 for rw.readers.ptr() != nil { 257 sched.Tracef("wake reader") 258 reader := rw.readers.ptr() 259 rw.readers = reader.schedlink 260 reader.schedlink.set(nil) 261 notewakeup(&reader.park) 262 r -= 1 263 } 264 // If r > 0, there are pending readers that aren't on the 265 // queue. Tell them to skip waiting. 266 rw.readerPass += uint32(r) 267 unlock(&rw.rLock) 268 // Allow other writers to proceed. 269 sched.Tracef("release wLock") 270 unlock(&rw.wLock) 271 }