github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/sync/mutex.go (about) 1 package sync 2 3 import ( 4 "internal/task" 5 _ "unsafe" 6 7 "runtime/volatile" 8 ) 9 10 type Mutex struct { 11 state uint8 // Set to non-zero if locked. 12 blocked task.Stack 13 } 14 15 //go:linkname scheduleTask runtime.runqueuePushBack 16 func scheduleTask(*task.Task) 17 18 func (m *Mutex) Lock() { 19 if m.islocked() { 20 // Push self onto stack of blocked tasks, and wait to be resumed. 21 m.blocked.Push(task.Current()) 22 task.Pause() 23 return 24 } 25 26 m.setlock(true) 27 } 28 29 func (m *Mutex) Unlock() { 30 if !m.islocked() { 31 panic("sync: unlock of unlocked Mutex") 32 } 33 34 // Wake up a blocked task, if applicable. 35 if t := m.blocked.Pop(); t != nil { 36 scheduleTask(t) 37 } else { 38 m.setlock(false) 39 } 40 } 41 42 // TryLock tries to lock m and reports whether it succeeded. 43 // 44 // Note that while correct uses of TryLock do exist, they are rare, 45 // and use of TryLock is often a sign of a deeper problem 46 // in a particular use of mutexes. 47 func (m *Mutex) TryLock() bool { 48 if m.islocked() { 49 return false 50 } 51 m.Lock() 52 return true 53 } 54 55 func (m *Mutex) islocked() bool { 56 return volatile.LoadUint8(&m.state) != 0 57 } 58 59 func (m *Mutex) setlock(b bool) { 60 volatile.StoreUint8(&m.state, boolToU8(b)) 61 } 62 63 func boolToU8(b bool) uint8 { 64 if b { 65 return 1 66 } 67 return 0 68 } 69 70 type RWMutex struct { 71 // waitingWriters are all of the tasks waiting for write locks. 72 waitingWriters task.Stack 73 74 // waitingReaders are all of the tasks waiting for a read lock. 75 waitingReaders task.Stack 76 77 // state is the current state of the RWMutex. 78 // Iff the mutex is completely unlocked, it contains rwMutexStateUnlocked (aka 0). 79 // Iff the mutex is write-locked, it contains rwMutexStateWLocked. 80 // While the mutex is read-locked, it contains the current number of readers. 81 state uint32 82 } 83 84 const ( 85 rwMutexStateUnlocked = uint32(0) 86 rwMutexStateWLocked = ^uint32(0) 87 rwMutexMaxReaders = rwMutexStateWLocked - 1 88 ) 89 90 func (rw *RWMutex) Lock() { 91 if rw.state == 0 { 92 // The mutex is completely unlocked. 93 // Lock without waiting. 94 rw.state = rwMutexStateWLocked 95 return 96 } 97 98 // Wait for the lock to be released. 99 rw.waitingWriters.Push(task.Current()) 100 task.Pause() 101 } 102 103 func (rw *RWMutex) Unlock() { 104 switch rw.state { 105 case rwMutexStateWLocked: 106 // This is correct. 107 108 case rwMutexStateUnlocked: 109 // The mutex is already unlocked. 110 panic("sync: unlock of unlocked RWMutex") 111 112 default: 113 // The mutex is read-locked instead of write-locked. 114 panic("sync: write-unlock of read-locked RWMutex") 115 } 116 117 switch { 118 case rw.maybeUnblockReaders(): 119 // Switched over to read mode. 120 121 case rw.maybeUnblockWriter(): 122 // Transferred to another writer. 123 124 default: 125 // Nothing is waiting for the lock. 126 rw.state = rwMutexStateUnlocked 127 } 128 } 129 130 func (rw *RWMutex) RLock() { 131 if rw.state == rwMutexStateWLocked { 132 // Wait for the write lock to be released. 133 rw.waitingReaders.Push(task.Current()) 134 task.Pause() 135 return 136 } 137 138 if rw.state == rwMutexMaxReaders { 139 panic("sync: too many readers on RWMutex") 140 } 141 142 // Increase the reader count. 143 rw.state++ 144 } 145 146 func (rw *RWMutex) RUnlock() { 147 switch rw.state { 148 case rwMutexStateUnlocked: 149 // The mutex is already unlocked. 150 panic("sync: unlock of unlocked RWMutex") 151 152 case rwMutexStateWLocked: 153 // The mutex is write-locked instead of read-locked. 154 panic("sync: read-unlock of write-locked RWMutex") 155 } 156 157 rw.state-- 158 159 if rw.state == rwMutexStateUnlocked { 160 // This was the last reader. 161 // Try to unblock a writer. 162 rw.maybeUnblockWriter() 163 } 164 } 165 166 func (rw *RWMutex) maybeUnblockReaders() bool { 167 var n uint32 168 for { 169 t := rw.waitingReaders.Pop() 170 if t == nil { 171 break 172 } 173 174 n++ 175 scheduleTask(t) 176 } 177 if n == 0 { 178 return false 179 } 180 181 rw.state = n 182 return true 183 } 184 185 func (rw *RWMutex) maybeUnblockWriter() bool { 186 t := rw.waitingWriters.Pop() 187 if t == nil { 188 return false 189 } 190 191 rw.state = rwMutexStateWLocked 192 scheduleTask(t) 193 194 return true 195 } 196 197 type Locker interface { 198 Lock() 199 Unlock() 200 } 201 202 // RLocker returns a Locker interface that implements 203 // the Lock and Unlock methods by calling rw.RLock and rw.RUnlock. 204 func (rw *RWMutex) RLocker() Locker { 205 return (*rlocker)(rw) 206 } 207 208 type rlocker RWMutex 209 210 func (r *rlocker) Lock() { (*RWMutex)(r).RLock() } 211 func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }