github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/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 // 暴露的Semaphore实现 7 // Intended use is provide a sleep and wakeup 8 // primitive that can be used in the contended case 9 // of other synchronization primitives. 10 // Thus it targets the same goal as Linux's futex, 11 // but it has much simpler semantics. 12 // 目地用处是提供一个睡眠和唤醒的操作, 其可以用于其他同步原语的原语 13 // 因此, 其目地和linux的futex是一样的, 只是有者更简单的语义 14 // 15 // That is, don't think of these as semaphores. 16 // Think of them as a way to implement sleep and wakeup 17 // such that every sleep is paired with a single wakeup, 18 // even if, due to races, the wakeup happens before the sleep. 19 // 20 // See Mullender and Cox, ``Semaphores in Plan 9,'' 21 // http://swtch.com/semaphore.pdf 22 23 // 用到了树堆(treap=tree+heap)这种数据结构, 其结构相当于随机数据插入的二叉搜索树, 实现简单且能基本实现随机平衡结构, 这里用最小堆 24 25 package runtime 26 27 import ( 28 "runtime/internal/atomic" 29 "runtime/internal/sys" 30 "unsafe" 31 ) 32 33 // Asynchronous semaphore for sync.Mutex. 34 35 // A semaRoot holds a balanced tree of sudog with distinct addresses (s.elem). 36 // Each of those sudog may in turn point (through s.waitlink) to a list 37 // of other sudogs waiting on the same address. 38 // The operations on the inner lists of sudogs with the same address 39 // are all O(1). The scanning of the top-level semaRoot list is O(log n), 40 // where n is the number of distinct addresses with goroutines blocked 41 // on them that hash to the given semaRoot. 42 // See golang.org/issue/17953 for a program that worked badly 43 // before we introduced the second level of list, and test/locklinear.go 44 // for a test that exercises this. 45 type semaRoot struct { 46 lock mutex 47 treap *sudog // root of balanced tree of unique waiters. 48 nwait uint32 // Number of waiters. Read w/o the lock. 49 } 50 51 // Prime to not correlate with any user patterns. 52 const semTabSize = 251 53 54 var semtable [semTabSize]struct { 55 root semaRoot 56 pad [sys.CacheLineSize - unsafe.Sizeof(semaRoot{})]byte 57 } 58 59 //go:linkname sync_runtime_Semacquire sync.runtime_Semacquire 60 func sync_runtime_Semacquire(addr *uint32) { 61 semacquire1(addr, false, semaBlockProfile) 62 } 63 64 //go:linkname poll_runtime_Semacquire internal/poll.runtime_Semacquire 65 func poll_runtime_Semacquire(addr *uint32) { 66 semacquire1(addr, false, semaBlockProfile) 67 } 68 69 //go:linkname sync_runtime_Semrelease sync.runtime_Semrelease 70 func sync_runtime_Semrelease(addr *uint32, handoff bool) { 71 semrelease1(addr, handoff) 72 } 73 74 //go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex 75 func sync_runtime_SemacquireMutex(addr *uint32, lifo bool) { 76 semacquire1(addr, lifo, semaBlockProfile|semaMutexProfile) 77 } 78 79 //go:linkname poll_runtime_Semrelease internal/poll.runtime_Semrelease 80 func poll_runtime_Semrelease(addr *uint32) { 81 semrelease(addr) 82 } 83 84 func readyWithTime(s *sudog, traceskip int) { 85 if s.releasetime != 0 { 86 s.releasetime = cputicks() 87 } 88 goready(s.g, traceskip) 89 } 90 91 type semaProfileFlags int 92 93 const ( 94 semaBlockProfile semaProfileFlags = 1 << iota 95 semaMutexProfile 96 ) 97 98 // Called from runtime. 99 func semacquire(addr *uint32) { 100 semacquire1(addr, false, 0) 101 } 102 103 func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags) { 104 gp := getg() 105 if gp != gp.m.curg { 106 throw("semacquire not on the G stack") 107 } 108 109 // Easy case. 110 if cansemacquire(addr) { 111 return 112 } 113 114 // Harder case: 115 // increment waiter count 116 // try cansemacquire one more time, return if succeeded 117 // enqueue itself as a waiter 118 // sleep 119 // (waiter descriptor is dequeued by signaler) 120 s := acquireSudog() 121 root := semroot(addr) 122 t0 := int64(0) 123 s.releasetime = 0 124 s.acquiretime = 0 125 s.ticket = 0 126 if profile&semaBlockProfile != 0 && blockprofilerate > 0 { 127 t0 = cputicks() 128 s.releasetime = -1 129 } 130 if profile&semaMutexProfile != 0 && mutexprofilerate > 0 { 131 if t0 == 0 { 132 t0 = cputicks() 133 } 134 s.acquiretime = t0 135 } 136 for { 137 lock(&root.lock) 138 // Add ourselves to nwait to disable "easy case" in semrelease. 139 atomic.Xadd(&root.nwait, 1) 140 // Check cansemacquire to avoid missed wakeup. 141 if cansemacquire(addr) { 142 atomic.Xadd(&root.nwait, -1) 143 unlock(&root.lock) 144 break 145 } 146 // Any semrelease after the cansemacquire knows we're waiting 147 // (we set nwait above), so go to sleep. 148 root.queue(addr, s, lifo) 149 goparkunlock(&root.lock, "semacquire", traceEvGoBlockSync, 4) 150 if s.ticket != 0 || cansemacquire(addr) { 151 break 152 } 153 } 154 if s.releasetime > 0 { 155 blockevent(s.releasetime-t0, 3) 156 } 157 releaseSudog(s) 158 } 159 160 func semrelease(addr *uint32) { 161 semrelease1(addr, false) 162 } 163 164 func semrelease1(addr *uint32, handoff bool) { 165 root := semroot(addr) 166 atomic.Xadd(addr, 1) 167 168 // Easy case: no waiters? 169 // This check must happen after the xadd, to avoid a missed wakeup 170 // (see loop in semacquire). 171 if atomic.Load(&root.nwait) == 0 { 172 return 173 } 174 175 // Harder case: search for a waiter and wake it. 176 lock(&root.lock) 177 if atomic.Load(&root.nwait) == 0 { 178 // The count is already consumed by another goroutine, 179 // so no need to wake up another goroutine. 180 unlock(&root.lock) 181 return 182 } 183 s, t0 := root.dequeue(addr) 184 if s != nil { 185 atomic.Xadd(&root.nwait, -1) 186 } 187 unlock(&root.lock) 188 if s != nil { // May be slow, so unlock first 189 acquiretime := s.acquiretime 190 if acquiretime != 0 { 191 mutexevent(t0-acquiretime, 3) 192 } 193 if s.ticket != 0 { 194 throw("corrupted semaphore ticket") 195 } 196 if handoff && cansemacquire(addr) { 197 s.ticket = 1 198 } 199 readyWithTime(s, 5) 200 } 201 } 202 203 func semroot(addr *uint32) *semaRoot { 204 return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root 205 } 206 207 // addr指向一个值(信号量), 如果这个值>0则表示有资源, 直接cas -1即可 208 // 如果为0则表示没有资源 209 func cansemacquire(addr *uint32) bool { 210 for { 211 v := atomic.Load(addr) 212 if v == 0 { 213 return false 214 } 215 if atomic.Cas(addr, v, v-1) { 216 return true 217 } 218 } 219 } 220 221 // queue adds s to the blocked goroutines in semaRoot. 222 // 二叉搜索树插入一个节点 223 func (root *semaRoot) queue(addr *uint32, s *sudog, lifo bool) { 224 s.g = getg() 225 s.elem = unsafe.Pointer(addr) 226 s.next = nil 227 s.prev = nil 228 229 var last *sudog 230 pt := &root.treap 231 for t := *pt; t != nil; t = *pt { 232 if t.elem == unsafe.Pointer(addr) { 233 // Already have addr in list. 234 if lifo { 235 // Substitute s in t's place in treap. 236 *pt = s 237 s.ticket = t.ticket 238 s.acquiretime = t.acquiretime 239 s.parent = t.parent 240 s.prev = t.prev 241 s.next = t.next 242 if s.prev != nil { 243 s.prev.parent = s 244 } 245 if s.next != nil { 246 s.next.parent = s 247 } 248 // Add t first in s's wait list. 249 s.waitlink = t 250 s.waittail = t.waittail 251 if s.waittail == nil { 252 s.waittail = t 253 } 254 t.parent = nil 255 t.prev = nil 256 t.next = nil 257 t.waittail = nil 258 } else { 259 // Add s to end of t's wait list. 260 if t.waittail == nil { 261 t.waitlink = s 262 } else { 263 t.waittail.waitlink = s 264 } 265 t.waittail = s 266 s.waitlink = nil 267 } 268 return 269 } 270 last = t 271 if uintptr(unsafe.Pointer(addr)) < uintptr(t.elem) { 272 pt = &t.prev 273 } else { 274 pt = &t.next 275 } 276 } 277 278 // Add s as new leaf in tree of unique addrs. 279 // The balanced tree is a treap using ticket as the random heap priority. 280 // That is, it is a binary tree ordered according to the elem addresses, 281 // but then among the space of possible binary trees respecting those 282 // addresses, it is kept balanced on average by maintaining a heap ordering 283 // on the ticket: s.ticket <= both s.prev.ticket and s.next.ticket. 284 // https://en.wikipedia.org/wiki/Treap 285 // http://faculty.washington.edu/aragon/pubs/rst89.pdf 286 // 287 // s.ticket compared with zero in couple of places, therefore set lowest bit. 288 // It will not affect treap's quality noticeably. 289 s.ticket = fastrand() | 1 290 s.parent = last 291 *pt = s 292 293 // Rotate up into tree according to ticket (priority). 294 for s.parent != nil && s.parent.ticket > s.ticket { 295 if s.parent.prev == s { 296 root.rotateRight(s.parent) 297 } else { 298 if s.parent.next != s { // 用一些断言保证树没什么问题 299 panic("semaRoot queue") 300 } 301 root.rotateLeft(s.parent) 302 } 303 } 304 } 305 306 // dequeue searches for and finds the first goroutine 307 // in semaRoot blocked on addr. 308 // If the sudog was being profiled, dequeue returns the time 309 // at which it was woken up as now. Otherwise now is 0. 310 // 如果这个addr里有多个元素, 则只dequeue第一个 311 func (root *semaRoot) dequeue(addr *uint32) (found *sudog, now int64) { 312 ps := &root.treap 313 s := *ps 314 for ; s != nil; s = *ps { 315 if s.elem == unsafe.Pointer(addr) { 316 goto Found 317 } 318 if uintptr(unsafe.Pointer(addr)) < uintptr(s.elem) { 319 ps = &s.prev 320 } else { 321 ps = &s.next 322 } 323 } 324 return nil, 0 325 326 Found: 327 now = int64(0) 328 if s.acquiretime != 0 { 329 now = cputicks() 330 } 331 if t := s.waitlink; t != nil { 332 // 有多于一个元素, 则树不用修改, 改这个节点链表即可 333 // Substitute t, also waiting on addr, for s in root tree of unique addrs. 334 *ps = t 335 t.ticket = s.ticket 336 t.parent = s.parent 337 t.prev = s.prev 338 if t.prev != nil { 339 t.prev.parent = t 340 } 341 t.next = s.next 342 if t.next != nil { 343 t.next.parent = t 344 } 345 if t.waitlink != nil { 346 t.waittail = s.waittail 347 } else { 348 t.waittail = nil 349 } 350 t.acquiretime = now 351 s.waitlink = nil 352 s.waittail = nil 353 } else { 354 // Rotate s down to be leaf of tree for removal, respecting priorities. 355 for s.next != nil || s.prev != nil { 356 if s.next == nil || s.prev != nil && s.prev.ticket < s.next.ticket { 357 root.rotateRight(s) 358 } else { 359 root.rotateLeft(s) 360 } 361 } 362 // Remove s, now a leaf. 363 if s.parent != nil { 364 if s.parent.prev == s { 365 s.parent.prev = nil 366 } else { 367 s.parent.next = nil 368 } 369 } else { 370 root.treap = nil 371 } 372 } 373 s.parent = nil 374 s.elem = nil 375 s.next = nil 376 s.prev = nil 377 s.ticket = 0 378 return s, now 379 } 380 381 // rotateLeft rotates the tree rooted at node x. 382 // turning (x a (y b c)) into (y (x a b) c). 383 func (root *semaRoot) rotateLeft(x *sudog) { 384 // p -> (x a (y b c)) 385 p := x.parent 386 a, y := x.prev, x.next 387 b, c := y.prev, y.next 388 389 y.prev = x 390 x.parent = y 391 y.next = c 392 if c != nil { 393 c.parent = y 394 } 395 x.prev = a 396 if a != nil { 397 a.parent = x 398 } 399 x.next = b 400 if b != nil { 401 b.parent = x 402 } 403 404 y.parent = p 405 if p == nil { 406 root.treap = y 407 } else if p.prev == x { 408 p.prev = y 409 } else { 410 if p.next != x { 411 throw("semaRoot rotateLeft") 412 } 413 p.next = y 414 } 415 } 416 417 // rotateRight rotates the tree rooted at node y. 418 // turning (y (x a b) c) into (x a (y b c)). 419 func (root *semaRoot) rotateRight(y *sudog) { 420 // p -> (y (x a b) c) 421 p := y.parent 422 x, c := y.prev, y.next 423 a, b := x.prev, x.next 424 425 x.prev = a 426 if a != nil { 427 a.parent = x 428 } 429 x.next = y 430 y.parent = x 431 y.prev = b 432 if b != nil { 433 b.parent = y 434 } 435 y.next = c 436 if c != nil { 437 c.parent = y 438 } 439 440 x.parent = p 441 if p == nil { 442 root.treap = x 443 } else if p.prev == y { 444 p.prev = x 445 } else { 446 if p.next != y { 447 throw("semaRoot rotateRight") 448 } 449 p.next = x 450 } 451 } 452 453 // notifyList is a ticket-based notification list used to implement sync.Cond. 454 // 基于ticket的提醒队列, 用来实现sync.Cond 455 // 456 // It must be kept in sync with the sync package. 457 type notifyList struct { 458 // wait is the ticket number of the next waiter. It is atomically 459 // incremented outside the lock. 460 // 下一个等待者的ticket number, 总是>=notify 461 wait uint32 462 463 // notify is the ticket number of the next waiter to be notified. It can 464 // be read outside the lock, but is only written to with lock held. 465 // 这是下一个会被notify的waiter的ticket number 466 // 467 // Both wait & notify can wrap around, and such cases will be correctly 468 // handled as long as their "unwrapped" difference is bounded by 2^31. 469 // For this not to be the case, we'd need to have 2^31+ goroutines 470 // blocked on the same condvar, which is currently not possible. 471 notify uint32 472 473 // List of parked waiters. 474 lock mutex 475 head *sudog 476 tail *sudog 477 } 478 479 // less checks if a < b, considering a & b running counts that may overflow the 480 // 32-bit range, and that their "unwrapped" difference is always less than 2^31. 481 func less(a, b uint32) bool { 482 return int32(a-b) < 0 483 } 484 485 // notifyListAdd adds the caller to a notify list such that it can receive 486 // notifications. The caller must eventually call notifyListWait to wait for 487 // such a notification, passing the returned ticket number. 488 //go:linkname notifyListAdd sync.runtime_notifyListAdd 489 func notifyListAdd(l *notifyList) uint32 { 490 // This may be called concurrently, for example, when called from 491 // sync.Cond.Wait while holding a RWMutex in read mode. 492 return atomic.Xadd(&l.wait, 1) - 1 493 } 494 495 // notifyListWait waits for a notification. If one has been sent since 496 // notifyListAdd was called, it returns immediately. Otherwise, it blocks. 497 //go:linkname notifyListWait sync.runtime_notifyListWait 498 func notifyListWait(l *notifyList, t uint32) { 499 lock(&l.lock) 500 501 // Return right away if this ticket has already been notified. 502 // why not just t < l.notify 503 if less(t, l.notify) { 504 unlock(&l.lock) 505 return 506 } 507 508 // Enqueue itself. 509 s := acquireSudog() 510 s.g = getg() 511 s.ticket = t 512 s.releasetime = 0 513 t0 := int64(0) 514 if blockprofilerate > 0 { 515 t0 = cputicks() 516 s.releasetime = -1 517 } 518 if l.tail == nil { 519 l.head = s 520 } else { 521 l.tail.next = s 522 } 523 l.tail = s 524 goparkunlock(&l.lock, "semacquire", traceEvGoBlockCond, 3) 525 if t0 != 0 { 526 blockevent(s.releasetime-t0, 2) 527 } 528 releaseSudog(s) 529 } 530 531 // notifyListNotifyAll notifies all entries in the list. 532 //go:linkname notifyListNotifyAll sync.runtime_notifyListNotifyAll 533 func notifyListNotifyAll(l *notifyList) { 534 // Fast-path: if there are no new waiters since the last notification 535 // we don't need to acquire the lock. 536 if atomic.Load(&l.wait) == atomic.Load(&l.notify) { 537 return 538 } 539 540 // Pull the list out into a local variable, waiters will be readied 541 // outside the lock. 542 lock(&l.lock) 543 s := l.head 544 l.head = nil 545 l.tail = nil 546 547 // Update the next ticket to be notified. We can set it to the current 548 // value of wait because any previous waiters are already in the list 549 // or will notice that they have already been notified when trying to 550 // add themselves to the list. 551 atomic.Store(&l.notify, atomic.Load(&l.wait)) 552 unlock(&l.lock) 553 554 // Go through the local list and ready all waiters. 555 for s != nil { 556 next := s.next 557 s.next = nil 558 readyWithTime(s, 4) 559 s = next 560 } 561 } 562 563 // notifyListNotifyOne notifies one entry in the list. 564 //go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOne 565 func notifyListNotifyOne(l *notifyList) { 566 // Fast-path: if there are no new waiters since the last notification 567 // we don't need to acquire the lock at all. 568 if atomic.Load(&l.wait) == atomic.Load(&l.notify) { 569 return 570 } 571 572 lock(&l.lock) 573 574 // Re-check under the lock if we need to do anything. 575 t := l.notify 576 if t == atomic.Load(&l.wait) { 577 unlock(&l.lock) 578 return 579 } 580 581 // Update the next notify ticket number. 582 atomic.Store(&l.notify, t+1) 583 584 // Try to find the g that needs to be notified. 585 // If it hasn't made it to the list yet we won't find it, 586 // but it won't park itself once it sees the new notify number. 587 // 588 // This scan looks linear but essentially always stops quickly. 589 // Because g's queue separately from taking numbers, 590 // there may be minor reorderings in the list, but we 591 // expect the g we're looking for to be near the front. 592 // The g has others in front of it on the list only to the 593 // extent that it lost the race, so the iteration will not 594 // be too long. This applies even when the g is missing: 595 // it hasn't yet gotten to sleep and has lost the race to 596 // the (few) other g's that we find on the list. 597 for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next { 598 if s.ticket == t { 599 n := s.next 600 if p != nil { 601 p.next = n 602 } else { 603 l.head = n 604 } 605 if n == nil { 606 l.tail = p 607 } 608 unlock(&l.lock) 609 s.next = nil 610 readyWithTime(s, 4) 611 return 612 } 613 } 614 unlock(&l.lock) 615 } 616 617 //go:linkname notifyListCheck sync.runtime_notifyListCheck 618 func notifyListCheck(sz uintptr) { 619 if sz != unsafe.Sizeof(notifyList{}) { 620 print("runtime: bad notifyList size - sync=", sz, " runtime=", unsafe.Sizeof(notifyList{}), "\n") 621 throw("bad notifyList size") 622 } 623 } 624 625 //go:linkname sync_nanotime sync.runtime_nanotime 626 func sync_nanotime() int64 { 627 return nanotime() 628 }