github.com/sbinet/go@v0.0.0-20160827155028-54d7de7dd62b/src/runtime/sema.go (about) 1 // Copyright 2009 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 // Semaphore implementation exposed to Go. 6 // Intended use is provide a sleep and wakeup 7 // primitive that can be used in the contended case 8 // of other synchronization primitives. 9 // Thus it targets the same goal as Linux's futex, 10 // but it has much simpler semantics. 11 // 12 // That is, don't think of these as semaphores. 13 // Think of them as a way to implement sleep and wakeup 14 // such that every sleep is paired with a single wakeup, 15 // even if, due to races, the wakeup happens before the sleep. 16 // 17 // See Mullender and Cox, ``Semaphores in Plan 9,'' 18 // http://swtch.com/semaphore.pdf 19 20 package runtime 21 22 import ( 23 "runtime/internal/atomic" 24 "runtime/internal/sys" 25 "unsafe" 26 ) 27 28 // Asynchronous semaphore for sync.Mutex. 29 30 type semaRoot struct { 31 lock mutex 32 head *sudog 33 tail *sudog 34 nwait uint32 // Number of waiters. Read w/o the lock. 35 } 36 37 // Prime to not correlate with any user patterns. 38 const semTabSize = 251 39 40 var semtable [semTabSize]struct { 41 root semaRoot 42 pad [sys.CacheLineSize - unsafe.Sizeof(semaRoot{})]byte 43 } 44 45 //go:linkname sync_runtime_Semacquire sync.runtime_Semacquire 46 func sync_runtime_Semacquire(addr *uint32) { 47 semacquire(addr, true) 48 } 49 50 //go:linkname net_runtime_Semacquire net.runtime_Semacquire 51 func net_runtime_Semacquire(addr *uint32) { 52 semacquire(addr, true) 53 } 54 55 //go:linkname sync_runtime_Semrelease sync.runtime_Semrelease 56 func sync_runtime_Semrelease(addr *uint32) { 57 semrelease(addr) 58 } 59 60 //go:linkname net_runtime_Semrelease net.runtime_Semrelease 61 func net_runtime_Semrelease(addr *uint32) { 62 semrelease(addr) 63 } 64 65 func readyWithTime(s *sudog, traceskip int) { 66 if s.releasetime != 0 { 67 s.releasetime = cputicks() 68 } 69 goready(s.g, traceskip) 70 } 71 72 // Called from runtime. 73 func semacquire(addr *uint32, profile bool) { 74 gp := getg() 75 if gp != gp.m.curg { 76 throw("semacquire not on the G stack") 77 } 78 79 // Easy case. 80 if cansemacquire(addr) { 81 return 82 } 83 84 // Harder case: 85 // increment waiter count 86 // try cansemacquire one more time, return if succeeded 87 // enqueue itself as a waiter 88 // sleep 89 // (waiter descriptor is dequeued by signaler) 90 s := acquireSudog() 91 root := semroot(addr) 92 t0 := int64(0) 93 s.releasetime = 0 94 if profile && blockprofilerate > 0 { 95 t0 = cputicks() 96 s.releasetime = -1 97 } 98 for { 99 lock(&root.lock) 100 // Add ourselves to nwait to disable "easy case" in semrelease. 101 atomic.Xadd(&root.nwait, 1) 102 // Check cansemacquire to avoid missed wakeup. 103 if cansemacquire(addr) { 104 atomic.Xadd(&root.nwait, -1) 105 unlock(&root.lock) 106 break 107 } 108 // Any semrelease after the cansemacquire knows we're waiting 109 // (we set nwait above), so go to sleep. 110 root.queue(addr, s) 111 goparkunlock(&root.lock, "semacquire", traceEvGoBlockSync, 4) 112 if cansemacquire(addr) { 113 break 114 } 115 } 116 if s.releasetime > 0 { 117 blockevent(s.releasetime-t0, 3) 118 } 119 releaseSudog(s) 120 } 121 122 func semrelease(addr *uint32) { 123 root := semroot(addr) 124 atomic.Xadd(addr, 1) 125 126 // Easy case: no waiters? 127 // This check must happen after the xadd, to avoid a missed wakeup 128 // (see loop in semacquire). 129 if atomic.Load(&root.nwait) == 0 { 130 return 131 } 132 133 // Harder case: search for a waiter and wake it. 134 lock(&root.lock) 135 if atomic.Load(&root.nwait) == 0 { 136 // The count is already consumed by another goroutine, 137 // so no need to wake up another goroutine. 138 unlock(&root.lock) 139 return 140 } 141 s := root.head 142 for ; s != nil; s = s.next { 143 if s.elem == unsafe.Pointer(addr) { 144 atomic.Xadd(&root.nwait, -1) 145 root.dequeue(s) 146 break 147 } 148 } 149 unlock(&root.lock) 150 if s != nil { 151 readyWithTime(s, 5) 152 } 153 } 154 155 func semroot(addr *uint32) *semaRoot { 156 return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root 157 } 158 159 func cansemacquire(addr *uint32) bool { 160 for { 161 v := atomic.Load(addr) 162 if v == 0 { 163 return false 164 } 165 if atomic.Cas(addr, v, v-1) { 166 return true 167 } 168 } 169 } 170 171 func (root *semaRoot) queue(addr *uint32, s *sudog) { 172 s.g = getg() 173 s.elem = unsafe.Pointer(addr) 174 s.next = nil 175 s.prev = root.tail 176 if root.tail != nil { 177 root.tail.next = s 178 } else { 179 root.head = s 180 } 181 root.tail = s 182 } 183 184 func (root *semaRoot) dequeue(s *sudog) { 185 if s.next != nil { 186 s.next.prev = s.prev 187 } else { 188 root.tail = s.prev 189 } 190 if s.prev != nil { 191 s.prev.next = s.next 192 } else { 193 root.head = s.next 194 } 195 s.elem = nil 196 s.next = nil 197 s.prev = nil 198 } 199 200 // notifyList is a ticket-based notification list used to implement sync.Cond. 201 // 202 // It must be kept in sync with the sync package. 203 type notifyList struct { 204 // wait is the ticket number of the next waiter. It is atomically 205 // incremented outside the lock. 206 wait uint32 207 208 // notify is the ticket number of the next waiter to be notified. It can 209 // be read outside the lock, but is only written to with lock held. 210 // 211 // Both wait & notify can wrap around, and such cases will be correctly 212 // handled as long as their "unwrapped" difference is bounded by 2^31. 213 // For this not to be the case, we'd need to have 2^31+ goroutines 214 // blocked on the same condvar, which is currently not possible. 215 notify uint32 216 217 // List of parked waiters. 218 lock mutex 219 head *sudog 220 tail *sudog 221 } 222 223 // less checks if a < b, considering a & b running counts that may overflow the 224 // 32-bit range, and that their "unwrapped" difference is always less than 2^31. 225 func less(a, b uint32) bool { 226 return int32(a-b) < 0 227 } 228 229 // notifyListAdd adds the caller to a notify list such that it can receive 230 // notifications. The caller must eventually call notifyListWait to wait for 231 // such a notification, passing the returned ticket number. 232 //go:linkname notifyListAdd sync.runtime_notifyListAdd 233 func notifyListAdd(l *notifyList) uint32 { 234 // This may be called concurrently, for example, when called from 235 // sync.Cond.Wait while holding a RWMutex in read mode. 236 return atomic.Xadd(&l.wait, 1) - 1 237 } 238 239 // notifyListWait waits for a notification. If one has been sent since 240 // notifyListAdd was called, it returns immediately. Otherwise, it blocks. 241 //go:linkname notifyListWait sync.runtime_notifyListWait 242 func notifyListWait(l *notifyList, t uint32) { 243 lock(&l.lock) 244 245 // Return right away if this ticket has already been notified. 246 if less(t, l.notify) { 247 unlock(&l.lock) 248 return 249 } 250 251 // Enqueue itself. 252 s := acquireSudog() 253 s.g = getg() 254 s.ticket = t 255 s.releasetime = 0 256 t0 := int64(0) 257 if blockprofilerate > 0 { 258 t0 = cputicks() 259 s.releasetime = -1 260 } 261 if l.tail == nil { 262 l.head = s 263 } else { 264 l.tail.next = s 265 } 266 l.tail = s 267 goparkunlock(&l.lock, "semacquire", traceEvGoBlockCond, 3) 268 if t0 != 0 { 269 blockevent(s.releasetime-t0, 2) 270 } 271 releaseSudog(s) 272 } 273 274 // notifyListNotifyAll notifies all entries in the list. 275 //go:linkname notifyListNotifyAll sync.runtime_notifyListNotifyAll 276 func notifyListNotifyAll(l *notifyList) { 277 // Fast-path: if there are no new waiters since the last notification 278 // we don't need to acquire the lock. 279 if atomic.Load(&l.wait) == atomic.Load(&l.notify) { 280 return 281 } 282 283 // Pull the list out into a local variable, waiters will be readied 284 // outside the lock. 285 lock(&l.lock) 286 s := l.head 287 l.head = nil 288 l.tail = nil 289 290 // Update the next ticket to be notified. We can set it to the current 291 // value of wait because any previous waiters are already in the list 292 // or will notice that they have already been notified when trying to 293 // add themselves to the list. 294 atomic.Store(&l.notify, atomic.Load(&l.wait)) 295 unlock(&l.lock) 296 297 // Go through the local list and ready all waiters. 298 for s != nil { 299 next := s.next 300 s.next = nil 301 readyWithTime(s, 4) 302 s = next 303 } 304 } 305 306 // notifyListNotifyOne notifies one entry in the list. 307 //go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOne 308 func notifyListNotifyOne(l *notifyList) { 309 // Fast-path: if there are no new waiters since the last notification 310 // we don't need to acquire the lock at all. 311 if atomic.Load(&l.wait) == atomic.Load(&l.notify) { 312 return 313 } 314 315 lock(&l.lock) 316 317 // Re-check under the lock if we need to do anything. 318 t := l.notify 319 if t == atomic.Load(&l.wait) { 320 unlock(&l.lock) 321 return 322 } 323 324 // Update the next notify ticket number, and try to find the G that 325 // needs to be notified. If it hasn't made it to the list yet we won't 326 // find it, but it won't park itself once it sees the new notify number. 327 atomic.Store(&l.notify, t+1) 328 for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next { 329 if s.ticket == t { 330 n := s.next 331 if p != nil { 332 p.next = n 333 } else { 334 l.head = n 335 } 336 if n == nil { 337 l.tail = p 338 } 339 unlock(&l.lock) 340 s.next = nil 341 readyWithTime(s, 4) 342 return 343 } 344 } 345 unlock(&l.lock) 346 } 347 348 //go:linkname notifyListCheck sync.runtime_notifyListCheck 349 func notifyListCheck(sz uintptr) { 350 if sz != unsafe.Sizeof(notifyList{}) { 351 print("runtime: bad notifyList size - sync=", sz, " runtime=", unsafe.Sizeof(notifyList{}), "\n") 352 throw("bad notifyList size") 353 } 354 }