github.com/comwrg/go/src@v0.0.0-20220319063731-c238d0440370/runtime/lock_sema.go (about) 1 // Copyright 2011 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 //go:build aix || darwin || netbsd || openbsd || plan9 || solaris || windows 6 // +build aix darwin netbsd openbsd plan9 solaris windows 7 8 package runtime 9 10 import ( 11 "runtime/internal/atomic" 12 "unsafe" 13 ) 14 15 // This implementation depends on OS-specific implementations of 16 // 17 // func semacreate(mp *m) 18 // Create a semaphore for mp, if it does not already have one. 19 // 20 // func semasleep(ns int64) int32 21 // If ns < 0, acquire m's semaphore and return 0. 22 // If ns >= 0, try to acquire m's semaphore for at most ns nanoseconds. 23 // Return 0 if the semaphore was acquired, -1 if interrupted or timed out. 24 // 25 // func semawakeup(mp *m) 26 // Wake up mp, which is or will soon be sleeping on its semaphore. 27 // 28 const ( 29 locked uintptr = 1 30 31 active_spin = 4 32 active_spin_cnt = 30 33 passive_spin = 1 34 ) 35 36 func lock(l *mutex) { 37 lockWithRank(l, getLockRank(l)) 38 } 39 40 func lock2(l *mutex) { 41 gp := getg() 42 if gp.m.locks < 0 { 43 throw("runtime·lock: lock count") 44 } 45 gp.m.locks++ 46 47 // Speculative grab for lock. 48 if atomic.Casuintptr(&l.key, 0, locked) { 49 return 50 } 51 semacreate(gp.m) 52 53 // On uniprocessor's, no point spinning. 54 // On multiprocessors, spin for ACTIVE_SPIN attempts. 55 spin := 0 56 if ncpu > 1 { 57 spin = active_spin 58 } 59 Loop: 60 for i := 0; ; i++ { 61 v := atomic.Loaduintptr(&l.key) 62 if v&locked == 0 { 63 // Unlocked. Try to lock. 64 if atomic.Casuintptr(&l.key, v, v|locked) { 65 return 66 } 67 i = 0 68 } 69 if i < spin { 70 procyield(active_spin_cnt) 71 } else if i < spin+passive_spin { 72 osyield() 73 } else { 74 // Someone else has it. 75 // l->waitm points to a linked list of M's waiting 76 // for this lock, chained through m->nextwaitm. 77 // Queue this M. 78 for { 79 gp.m.nextwaitm = muintptr(v &^ locked) 80 if atomic.Casuintptr(&l.key, v, uintptr(unsafe.Pointer(gp.m))|locked) { 81 break 82 } 83 v = atomic.Loaduintptr(&l.key) 84 if v&locked == 0 { 85 continue Loop 86 } 87 } 88 if v&locked != 0 { 89 // Queued. Wait. 90 semasleep(-1) 91 i = 0 92 } 93 } 94 } 95 } 96 97 func unlock(l *mutex) { 98 unlockWithRank(l) 99 } 100 101 //go:nowritebarrier 102 // We might not be holding a p in this code. 103 func unlock2(l *mutex) { 104 gp := getg() 105 var mp *m 106 for { 107 v := atomic.Loaduintptr(&l.key) 108 if v == locked { 109 if atomic.Casuintptr(&l.key, locked, 0) { 110 break 111 } 112 } else { 113 // Other M's are waiting for the lock. 114 // Dequeue an M. 115 mp = muintptr(v &^ locked).ptr() 116 if atomic.Casuintptr(&l.key, v, uintptr(mp.nextwaitm)) { 117 // Dequeued an M. Wake it. 118 semawakeup(mp) 119 break 120 } 121 } 122 } 123 gp.m.locks-- 124 if gp.m.locks < 0 { 125 throw("runtime·unlock: lock count") 126 } 127 if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack 128 gp.stackguard0 = stackPreempt 129 } 130 } 131 132 // One-time notifications. 133 func noteclear(n *note) { 134 if GOOS == "aix" { 135 // On AIX, semaphores might not synchronize the memory in some 136 // rare cases. See issue #30189. 137 atomic.Storeuintptr(&n.key, 0) 138 } else { 139 n.key = 0 140 } 141 } 142 143 func notewakeup(n *note) { 144 var v uintptr 145 for { 146 v = atomic.Loaduintptr(&n.key) 147 if atomic.Casuintptr(&n.key, v, locked) { 148 break 149 } 150 } 151 152 // Successfully set waitm to locked. 153 // What was it before? 154 switch { 155 case v == 0: 156 // Nothing was waiting. Done. 157 case v == locked: 158 // Two notewakeups! Not allowed. 159 throw("notewakeup - double wakeup") 160 default: 161 // Must be the waiting m. Wake it up. 162 semawakeup((*m)(unsafe.Pointer(v))) 163 } 164 } 165 166 func notesleep(n *note) { 167 gp := getg() 168 if gp != gp.m.g0 { 169 throw("notesleep not on g0") 170 } 171 semacreate(gp.m) 172 if !atomic.Casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) { 173 // Must be locked (got wakeup). 174 if n.key != locked { 175 throw("notesleep - waitm out of sync") 176 } 177 return 178 } 179 // Queued. Sleep. 180 gp.m.blocked = true 181 if *cgo_yield == nil { 182 semasleep(-1) 183 } else { 184 // Sleep for an arbitrary-but-moderate interval to poll libc interceptors. 185 const ns = 10e6 186 for atomic.Loaduintptr(&n.key) == 0 { 187 semasleep(ns) 188 asmcgocall(*cgo_yield, nil) 189 } 190 } 191 gp.m.blocked = false 192 } 193 194 //go:nosplit 195 func notetsleep_internal(n *note, ns int64, gp *g, deadline int64) bool { 196 // gp and deadline are logically local variables, but they are written 197 // as parameters so that the stack space they require is charged 198 // to the caller. 199 // This reduces the nosplit footprint of notetsleep_internal. 200 gp = getg() 201 202 // Register for wakeup on n->waitm. 203 if !atomic.Casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) { 204 // Must be locked (got wakeup). 205 if n.key != locked { 206 throw("notetsleep - waitm out of sync") 207 } 208 return true 209 } 210 if ns < 0 { 211 // Queued. Sleep. 212 gp.m.blocked = true 213 if *cgo_yield == nil { 214 semasleep(-1) 215 } else { 216 // Sleep in arbitrary-but-moderate intervals to poll libc interceptors. 217 const ns = 10e6 218 for semasleep(ns) < 0 { 219 asmcgocall(*cgo_yield, nil) 220 } 221 } 222 gp.m.blocked = false 223 return true 224 } 225 226 deadline = nanotime() + ns 227 for { 228 // Registered. Sleep. 229 gp.m.blocked = true 230 if *cgo_yield != nil && ns > 10e6 { 231 ns = 10e6 232 } 233 if semasleep(ns) >= 0 { 234 gp.m.blocked = false 235 // Acquired semaphore, semawakeup unregistered us. 236 // Done. 237 return true 238 } 239 if *cgo_yield != nil { 240 asmcgocall(*cgo_yield, nil) 241 } 242 gp.m.blocked = false 243 // Interrupted or timed out. Still registered. Semaphore not acquired. 244 ns = deadline - nanotime() 245 if ns <= 0 { 246 break 247 } 248 // Deadline hasn't arrived. Keep sleeping. 249 } 250 251 // Deadline arrived. Still registered. Semaphore not acquired. 252 // Want to give up and return, but have to unregister first, 253 // so that any notewakeup racing with the return does not 254 // try to grant us the semaphore when we don't expect it. 255 for { 256 v := atomic.Loaduintptr(&n.key) 257 switch v { 258 case uintptr(unsafe.Pointer(gp.m)): 259 // No wakeup yet; unregister if possible. 260 if atomic.Casuintptr(&n.key, v, 0) { 261 return false 262 } 263 case locked: 264 // Wakeup happened so semaphore is available. 265 // Grab it to avoid getting out of sync. 266 gp.m.blocked = true 267 if semasleep(-1) < 0 { 268 throw("runtime: unable to acquire - semaphore out of sync") 269 } 270 gp.m.blocked = false 271 return true 272 default: 273 throw("runtime: unexpected waitm - semaphore out of sync") 274 } 275 } 276 } 277 278 func notetsleep(n *note, ns int64) bool { 279 gp := getg() 280 if gp != gp.m.g0 { 281 throw("notetsleep not on g0") 282 } 283 semacreate(gp.m) 284 return notetsleep_internal(n, ns, nil, 0) 285 } 286 287 // same as runtime·notetsleep, but called on user g (not g0) 288 // calls only nosplit functions between entersyscallblock/exitsyscall 289 func notetsleepg(n *note, ns int64) bool { 290 gp := getg() 291 if gp == gp.m.g0 { 292 throw("notetsleepg on g0") 293 } 294 semacreate(gp.m) 295 entersyscallblock() 296 ok := notetsleep_internal(n, ns, nil, 0) 297 exitsyscall() 298 return ok 299 } 300 301 func beforeIdle(int64, int64) (*g, bool) { 302 return nil, false 303 } 304 305 func checkTimeouts() {}