github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/src/runtime/lock_futex.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 // +build dragonfly freebsd linux 6 7 package runtime 8 9 import ( 10 "runtime/internal/atomic" 11 "unsafe" 12 ) 13 14 // This implementation depends on OS-specific implementations of 15 // 16 // futexsleep(addr *uint32, val uint32, ns int64) 17 // Atomically, 18 // if *addr == val { sleep } 19 // Might be woken up spuriously; that's allowed. 20 // Don't sleep longer than ns; ns < 0 means forever. 21 // 22 // futexwakeup(addr *uint32, cnt uint32) 23 // If any procs are sleeping on addr, wake up at most cnt. 24 25 const ( 26 mutex_unlocked = 0 27 mutex_locked = 1 28 mutex_sleeping = 2 29 30 active_spin = 4 31 active_spin_cnt = 30 32 passive_spin = 1 33 ) 34 35 // Possible lock states are mutex_unlocked, mutex_locked and mutex_sleeping. 36 // mutex_sleeping means that there is presumably at least one sleeping thread. 37 // Note that there can be spinning threads during all states - they do not 38 // affect mutex's state. 39 40 // We use the uintptr mutex.key and note.key as a uint32. 41 //go:nosplit 42 func key32(p *uintptr) *uint32 { 43 return (*uint32)(unsafe.Pointer(p)) 44 } 45 46 func lock(l *mutex) { 47 gp := getg() 48 49 if gp.m.locks < 0 { 50 throw("runtime·lock: lock count") 51 } 52 gp.m.locks++ 53 54 // Speculative grab for lock. 55 v := atomic.Xchg(key32(&l.key), mutex_locked) 56 if v == mutex_unlocked { 57 return 58 } 59 60 // wait is either MUTEX_LOCKED or MUTEX_SLEEPING 61 // depending on whether there is a thread sleeping 62 // on this mutex. If we ever change l->key from 63 // MUTEX_SLEEPING to some other value, we must be 64 // careful to change it back to MUTEX_SLEEPING before 65 // returning, to ensure that the sleeping thread gets 66 // its wakeup call. 67 wait := v 68 69 // On uniprocessors, no point spinning. 70 // On multiprocessors, spin for ACTIVE_SPIN attempts. 71 spin := 0 72 if ncpu > 1 { 73 spin = active_spin 74 } 75 for { 76 // Try for lock, spinning. 77 for i := 0; i < spin; i++ { 78 for l.key == mutex_unlocked { 79 if atomic.Cas(key32(&l.key), mutex_unlocked, wait) { 80 return 81 } 82 } 83 procyield(active_spin_cnt) 84 } 85 86 // Try for lock, rescheduling. 87 for i := 0; i < passive_spin; i++ { 88 for l.key == mutex_unlocked { 89 if atomic.Cas(key32(&l.key), mutex_unlocked, wait) { 90 return 91 } 92 } 93 osyield() 94 } 95 96 // Sleep. 97 v = atomic.Xchg(key32(&l.key), mutex_sleeping) 98 if v == mutex_unlocked { 99 return 100 } 101 wait = mutex_sleeping 102 futexsleep(key32(&l.key), mutex_sleeping, -1) 103 } 104 } 105 106 func unlock(l *mutex) { 107 v := atomic.Xchg(key32(&l.key), mutex_unlocked) 108 if v == mutex_unlocked { 109 throw("unlock of unlocked lock") 110 } 111 if v == mutex_sleeping { 112 futexwakeup(key32(&l.key), 1) 113 } 114 115 gp := getg() 116 gp.m.locks-- 117 if gp.m.locks < 0 { 118 throw("runtime·unlock: lock count") 119 } 120 if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack 121 gp.stackguard0 = stackPreempt 122 } 123 } 124 125 // One-time notifications. 126 func noteclear(n *note) { 127 n.key = 0 128 } 129 130 func notewakeup(n *note) { 131 old := atomic.Xchg(key32(&n.key), 1) 132 if old != 0 { 133 print("notewakeup - double wakeup (", old, ")\n") 134 throw("notewakeup - double wakeup") 135 } 136 futexwakeup(key32(&n.key), 1) 137 } 138 139 func notesleep(n *note) { 140 gp := getg() 141 if gp != gp.m.g0 { 142 throw("notesleep not on g0") 143 } 144 ns := int64(-1) 145 if *cgo_yield != nil { 146 // Sleep for an arbitrary-but-moderate interval to poll libc interceptors. 147 ns = 10e6 148 } 149 for atomic.Load(key32(&n.key)) == 0 { 150 gp.m.blocked = true 151 futexsleep(key32(&n.key), 0, ns) 152 if *cgo_yield != nil { 153 asmcgocall(*cgo_yield, nil) 154 } 155 gp.m.blocked = false 156 } 157 } 158 159 // May run with m.p==nil if called from notetsleep, so write barriers 160 // are not allowed. 161 // 162 //go:nosplit 163 //go:nowritebarrier 164 func notetsleep_internal(n *note, ns int64) bool { 165 gp := getg() 166 167 if ns < 0 { 168 if *cgo_yield != nil { 169 // Sleep for an arbitrary-but-moderate interval to poll libc interceptors. 170 ns = 10e6 171 } 172 for atomic.Load(key32(&n.key)) == 0 { 173 gp.m.blocked = true 174 futexsleep(key32(&n.key), 0, ns) 175 if *cgo_yield != nil { 176 asmcgocall(*cgo_yield, nil) 177 } 178 gp.m.blocked = false 179 } 180 return true 181 } 182 183 if atomic.Load(key32(&n.key)) != 0 { 184 return true 185 } 186 187 deadline := nanotime() + ns 188 for { 189 if *cgo_yield != nil && ns > 10e6 { 190 ns = 10e6 191 } 192 gp.m.blocked = true 193 futexsleep(key32(&n.key), 0, ns) 194 if *cgo_yield != nil { 195 asmcgocall(*cgo_yield, nil) 196 } 197 gp.m.blocked = false 198 if atomic.Load(key32(&n.key)) != 0 { 199 break 200 } 201 now := nanotime() 202 if now >= deadline { 203 break 204 } 205 ns = deadline - now 206 } 207 return atomic.Load(key32(&n.key)) != 0 208 } 209 210 func notetsleep(n *note, ns int64) bool { 211 gp := getg() 212 if gp != gp.m.g0 && gp.m.preemptoff != "" { 213 throw("notetsleep not on g0") 214 } 215 216 return notetsleep_internal(n, ns) 217 } 218 219 // same as runtime·notetsleep, but called on user g (not g0) 220 // calls only nosplit functions between entersyscallblock/exitsyscall 221 func notetsleepg(n *note, ns int64) bool { 222 gp := getg() 223 if gp == gp.m.g0 { 224 throw("notetsleepg on g0") 225 } 226 227 entersyscallblock(0) 228 ok := notetsleep_internal(n, ns) 229 exitsyscall(0) 230 return ok 231 }