github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/timer/timing-heap.go (about) 1 /* For license and copyright information please see the LEGAL file in the code repository */ 2 3 package timer 4 5 import ( 6 "sync" 7 "sync/atomic" 8 "unsafe" 9 10 "github.com/GeniusesGroup/libgo/cpu" 11 "github.com/GeniusesGroup/libgo/race" 12 "github.com/GeniusesGroup/libgo/scheduler" 13 "github.com/GeniusesGroup/libgo/time/monotonic" 14 ) 15 16 // TimingHeap ... 17 // Active timers live in the timers field as heap structure. 18 // Inactive timers live there too temporarily, until they are removed. 19 // 20 // https://github.com/search?l=go&q=timer&type=Repositories 21 // https://github.com/RussellLuo/timingwheel/blob/master/delayqueue/delayqueue.go 22 type TimingHeap struct { 23 coreID uint64 // CPU core number this timing run on it 24 25 // The when field of the first entry on the timer heap. 26 // This is 0 if the timer heap is empty. 27 timer0When monotonic.Atomic 28 29 // The earliest known when field of a timer with 30 // timerModifiedEarlier status. Because the timer may have been 31 // modified again, there need not be any timer with this value. 32 // This is 0 if there are no timerModifiedEarlier timers. 33 timerModifiedEarliest monotonic.Atomic 34 35 // Number of timers in P's heap. 36 numTimers atomic.Int32 37 38 // Number of timerDeleted timers in P's heap. 39 deletedTimers atomic.Int32 40 41 // Race context used while executing timer functions. 42 timerRaceCtx uintptr 43 44 // Lock for timers. We normally access the timers while running 45 // on this TimingHeap, but the scheduler can also do it from a different P. 46 timersLock sync.Mutex 47 // Must hold timersLock to access. 48 // https://en.wikipedia.org/wiki/Heap_(data_structure)#Comparison_of_theoretic_bounds_for_variants 49 // Balancing a heap is done by th.siftUp or th.siftDown methods 50 timers []timerBucketHeap 51 } 52 53 type timerBucketHeap struct { 54 timer *Async 55 // Two reason to have timer when here: 56 // - hot cache to prevent dereference timer to get when field 57 // - It can be difference with timer when filed in timerModifiedXX status. 58 when monotonic.Time 59 } 60 61 func (th *TimingHeap) Init() { 62 // TODO::: let application flow choose timers init cap or force it? 63 // th.timers = make([]timerBucketHeap, 1024) 64 65 th.coreID = cpu.ActiveCoreID() 66 // th.timerRaceCtx = racegostart(abi.FuncPCABIInternal(th.runTimer) + sys.PCQuantum) 67 } 68 func (th *TimingHeap) Reinit() {} 69 70 // Deinit releases all of the resources associated with timers in specific CPU core and 71 // move them to other core that call deinit 72 func (th *TimingHeap) Deinit() { 73 if len(th.timers) > 0 { 74 th.timersLock.Lock() 75 76 var newCore = &poolByCores[cpu.ActiveCoreID()] 77 newCore.timersLock.Lock() 78 newCore.moveTimers(th.timers) 79 newCore.timersLock.Unlock() 80 81 th.timers = nil 82 th.numTimers.Store(0) 83 th.deletedTimers.Store(0) 84 th.timer0When.Store(0) 85 86 th.timersLock.Unlock() 87 } 88 } 89 90 // AddTimer adds t to the timers queue. 91 func (th *TimingHeap) AddTimer(t *Async) { 92 th.timersLock.Lock() 93 94 th.cleanTimers() 95 96 var timerWhen = t.when 97 t.timers = th 98 var i = len(th.timers) 99 th.timers = append(th.timers, timerBucketHeap{t, timerWhen}) 100 101 th.siftUpTimer(i) 102 if t == th.timers[0].timer { 103 th.timer0When.Store(timerWhen) 104 } 105 th.numTimers.Add(1) 106 107 th.timersLock.Unlock() 108 } 109 110 // deleteTimer removes timer i from the timers heap. 111 // It returns the smallest changed index in th.timers 112 // The caller must have locked the th.timersLock 113 func (th *TimingHeap) deleteTimer(i int) int { 114 th.timers[i].timer.timers = nil 115 116 var last = len(th.timers) - 1 117 if i != last { 118 th.timers[i] = th.timers[last] 119 } 120 th.timers[last].timer = nil 121 th.timers = th.timers[:last] 122 123 var smallestChanged = i 124 if i != last { 125 // Moving to i may have moved the last timer to a new parent, 126 // so sift up to preserve the heap guarantee. 127 smallestChanged = th.siftUpTimer(i) 128 th.siftDownTimer(i) 129 } 130 if i == 0 { 131 th.updateTimer0When() 132 } 133 134 var timerRemaining = th.numTimers.Add(-1) 135 if timerRemaining == 0 { 136 // If there are no timers, then clearly none are modified. 137 th.timerModifiedEarliest.Store(0) 138 } 139 return smallestChanged 140 } 141 142 // deleteTimer0 removes timer 0 from the timers heap. 143 // It reports whether it saw no problems due to races. 144 // The caller must have locked the th.timersLock 145 func (th *TimingHeap) deleteTimer0() { 146 th.timers[0].timer.timers = nil 147 148 var last = len(th.timers) - 1 149 if last > 0 { 150 th.timers[0] = th.timers[last] 151 } 152 th.timers[last].timer = nil 153 th.timers = th.timers[:last] 154 if last > 0 { 155 th.siftDownTimer(0) 156 } 157 th.updateTimer0When() 158 159 var timerRemaining = th.numTimers.Add(-1) 160 if timerRemaining == 0 { 161 // If there are no timers, then clearly none are modified. 162 th.timerModifiedEarliest.Store(0) 163 } 164 } 165 166 // cleanTimers cleans up the head of the timer queue. This speeds up 167 // programs that create and delete timers; leaving them in the heap 168 // slows down AddTimer. Reports whether no timer problems were found. 169 // The caller must have locked the th.timersLock 170 func (th *TimingHeap) cleanTimers() { 171 if len(th.timers) == 0 { 172 return 173 } 174 175 for { 176 // This loop can theoretically run for a while, and because 177 // it is holding timersLock it cannot be preempted. 178 // If someone is trying to preempt us, just return. 179 // We can clean the timers later. 180 // if gp.preemptStop { 181 // return 182 // } 183 184 var timer = th.timers[0].timer 185 var status = timer.status.Load() 186 switch status { 187 case status_Deleted: 188 if !timer.status.CompareAndSwap(status, status_Removing) { 189 continue 190 } 191 th.deleteTimer0() 192 if !timer.status.CompareAndSwap(status_Removing, status_Removed) { 193 badTimer() 194 } 195 th.deletedTimers.Add(-1) 196 case status_ModifiedEarlier, status_ModifiedLater: 197 if !timer.status.CompareAndSwap(status, status_Moving) { 198 continue 199 } 200 // Now we can change the when field of timerBucketHeap. 201 th.timers[0].when = timer.when 202 // Move timer to the right position. 203 th.deleteTimer0() 204 th.AddTimer(timer) 205 if !timer.status.CompareAndSwap(status_Moving, status_Waiting) { 206 badTimer() 207 } 208 default: 209 // Head of timers does not need adjustment. 210 return 211 } 212 } 213 } 214 215 // moveTimers moves a slice of timers to the timers heap. 216 // The slice has been taken from a different Timers. 217 // This is currently called when the world is stopped, but the caller 218 // is expected to have locked the th.timersLock 219 func (th *TimingHeap) moveTimers(timers []timerBucketHeap) { 220 for _, timerBucketHeap := range timers { 221 var timer = timerBucketHeap.timer 222 loop: 223 for { 224 var status = timer.status.Load() 225 switch status { 226 case status_Waiting, status_ModifiedEarlier, status_ModifiedLater: 227 if !timer.status.CompareAndSwap(status, status_Moving) { 228 continue 229 } 230 timer.timers = nil 231 th.AddTimer(timer) 232 if !timer.status.CompareAndSwap(status_Moving, status_Waiting) { 233 badTimer() 234 } 235 break loop 236 case status_Deleted: 237 if !timer.status.CompareAndSwap(status, status_Removed) { 238 continue 239 } 240 timer.timers = nil 241 // We no longer need this timer in the heap. 242 break loop 243 case status_Modifying: 244 // Loop until the modification is complete. 245 scheduler.Yield(scheduler.Thread_WaitReason_Preempted) 246 case status_Unset, status_Removed: 247 // We should not see these status values in a timers heap. 248 badTimer() 249 case status_Running, status_Removing, status_Moving: 250 // Some other P thinks it owns this timer, 251 // which should not happen. 252 badTimer() 253 default: 254 badTimer() 255 } 256 } 257 } 258 } 259 260 // adjustTimers looks through the timers for any timers that have been modified to run earlier, 261 // and puts them in the correct place in the heap. While looking for those timers, 262 // it also moves timers that have been modified to run later, and removes deleted timers. 263 // The caller must have locked the th.timersLock 264 func (th *TimingHeap) adjustTimers(now monotonic.Time) { 265 // If we haven't yet reached the time of the first status_ModifiedEarlier 266 // timer, don't do anything. This speeds up programs that adjust 267 // a lot of timers back and forth if the timers rarely expire. 268 // We'll postpone looking through all the adjusted timers until 269 // one would actually expire. 270 var first = th.timerModifiedEarliest.Load() 271 if first == 0 || first > now { 272 if verifyTimers { 273 th.verifyTimerHeap() 274 } 275 return 276 } 277 278 // We are going to clear all status_ModifiedEarlier timers. 279 th.timerModifiedEarliest.Store(0) 280 281 var moved []*Async 282 var timers = th.timers 283 var timersLen = len(timers) 284 for i := 0; i < timersLen; i++ { 285 var timer = timers[i].timer 286 var status = timer.status.Load() 287 switch status { 288 case status_Deleted: 289 if timer.status.CompareAndSwap(status, status_Removing) { 290 var changed = th.deleteTimer(i) 291 if !timer.status.CompareAndSwap(status_Removing, status_Removed) { 292 badTimer() 293 } 294 th.deletedTimers.Add(-1) 295 // Go back to the earliest changed heap entry. 296 // "- 1" because the loop will add 1. 297 i = changed - 1 298 } 299 case status_ModifiedEarlier, status_ModifiedLater: 300 if timer.status.CompareAndSwap(status, status_Moving) { 301 // Take t off the heap, and hold onto it. 302 // We don't add it back yet because the 303 // heap manipulation could cause our 304 // loop to skip some other timer. 305 var changed = th.deleteTimer(i) 306 moved = append(moved, timer) 307 // Go back to the earliest changed heap entry. 308 // "- 1" because the loop will add 1. 309 i = changed - 1 310 } 311 case status_Unset, status_Running, status_Removing, status_Removed, status_Moving: 312 badTimer() 313 case status_Waiting: 314 // OK, nothing to do. 315 case status_Modifying: 316 // Check again after modification is complete. 317 scheduler.Yield(scheduler.Thread_WaitReason_Preempted) 318 i-- 319 default: 320 badTimer() 321 } 322 } 323 324 if len(moved) > 0 { 325 th.addAdjustedTimers(moved) 326 } 327 328 if verifyTimers { 329 th.verifyTimerHeap() 330 } 331 } 332 333 // addAdjustedTimers adds any timers we adjusted in th.adjustTimers 334 // back to the timer heap. 335 func (th *TimingHeap) addAdjustedTimers(moved []*Async) { 336 for _, t := range moved { 337 th.AddTimer(t) 338 if !t.status.CompareAndSwap(status_Moving, status_Waiting) { 339 badTimer() 340 } 341 } 342 } 343 344 // runTimer examines the first timer in timers. If it is ready based on now, 345 // it runs the timer and removes or updates it. 346 // Returns 0 if it ran a timer, -1 if there are no more timers, or the time 347 // when the first timer should run. 348 // The caller must have locked the th.timersLock 349 // If a timer is run, this will temporarily unlock the timers. 350 func (th *TimingHeap) runTimer(now monotonic.Time) monotonic.Time { 351 for { 352 var timer = th.timers[0].timer 353 var status = timer.status.Load() 354 switch status { 355 case status_Waiting: 356 if timer.when > now { 357 // Not ready to run. 358 return timer.when 359 } 360 361 if !timer.status.CompareAndSwap(status, status_Running) { 362 continue 363 } 364 // Note that runOneTimer may temporarily unlock th.timersLock 365 th.runOneTimer(timer, now) 366 return 0 367 368 case status_Deleted: 369 if !timer.status.CompareAndSwap(status, status_Removing) { 370 continue 371 } 372 th.deleteTimer0() 373 if !timer.status.CompareAndSwap(status_Removing, status_Removed) { 374 badTimer() 375 } 376 th.deletedTimers.Add(-1) 377 if len(th.timers) == 0 { 378 return -1 379 } 380 381 case status_ModifiedEarlier, status_ModifiedLater: 382 if !timer.status.CompareAndSwap(status, status_Moving) { 383 continue 384 } 385 th.deleteTimer0() 386 th.AddTimer(timer) 387 if !timer.status.CompareAndSwap(status_Moving, status_Waiting) { 388 badTimer() 389 } 390 391 case status_Modifying: 392 // Wait for modification to complete. 393 scheduler.Yield(scheduler.Thread_WaitReason_Preempted) 394 case status_Unset, status_Removed: 395 // Should not see a new or inactive timer on the heap. 396 badTimer() 397 case status_Running, status_Removing, status_Moving: 398 // These should only be set when timers are locked, 399 // and we didn't do it. 400 badTimer() 401 default: 402 badTimer() 403 } 404 } 405 } 406 407 // runOneTimer runs a single timer. 408 // The caller must have locked the th.timersLock 409 // This will temporarily unlock the timers while running the timer function. 410 func (th *TimingHeap) runOneTimer(t *Async, now monotonic.Time) { 411 if race.DetectorEnabled { 412 race.AcquireCTX(th.timerRaceCtx, unsafe.Pointer(t)) 413 } 414 415 if t.period > 0 { 416 // Leave in heap but adjust next time to fire. 417 var delta = t.when.Since(now) 418 t.when.Add(t.period * (1 + -delta/t.period)) 419 if t.when < 0 { // check for overflow. 420 t.when = maxWhen 421 } 422 th.siftDownTimer(0) 423 if !t.status.CompareAndSwap(status_Running, status_Waiting) { 424 badTimer() 425 } 426 th.updateTimer0When() 427 } else { 428 // Remove from heap. 429 th.deleteTimer0() 430 if !t.status.CompareAndSwap(status_Running, status_Unset) { 431 badTimer() 432 } 433 } 434 435 if race.DetectorEnabled { 436 // Temporarily use the current th.timerRaceCtx for thread 437 scheduler.SetRaceCtx(th.timerRaceCtx) 438 } 439 440 var callback = t.callback 441 th.timersLock.Unlock() 442 callback.TimerHandler() 443 th.timersLock.Lock() 444 445 if race.DetectorEnabled { 446 scheduler.ReleaseRaceCtx() 447 } 448 } 449 450 // clearDeletedTimers removes all deleted timers from the timers heap. 451 // This is used to avoid clogging up the heap if the program 452 // starts a lot of long-running timers and then stops them. 453 // For example, this can happen via context.WithTimeout. 454 // 455 // This is the only function that walks through the entire timer heap, 456 // other than moveTimers which only runs when the world is stopped. 457 // 458 // The caller must have locked the th.timersLock 459 func (th *TimingHeap) clearDeletedTimers() { 460 // We are going to clear all status_ModifiedEarlier timers. 461 // Do this now in case new ones show up while we are looping. 462 th.timerModifiedEarliest.Store(0) 463 464 var cdel = int32(0) 465 var to = 0 466 var changedHeap = false 467 var timers = th.timers 468 var timersLen = len(timers) 469 nextTimer: 470 for i := 0; i < timersLen; i++ { 471 var timer = timers[i].timer 472 for { 473 var status = timer.status.Load() 474 switch status { 475 case status_Waiting: 476 if changedHeap { 477 timers[to] = timers[i] 478 th.siftUpTimer(to) 479 } 480 to++ 481 continue nextTimer 482 case status_ModifiedEarlier, status_ModifiedLater: 483 if timer.status.CompareAndSwap(status, status_Moving) { 484 timers[i].when = timer.when 485 timers[to] = timers[i] 486 th.siftUpTimer(to) 487 to++ 488 changedHeap = true 489 if !timer.status.CompareAndSwap(status_Moving, status_Waiting) { 490 badTimer() 491 } 492 continue nextTimer 493 } 494 case status_Deleted: 495 if timer.status.CompareAndSwap(status, status_Removing) { 496 timer.timers = nil 497 cdel++ 498 if !timer.status.CompareAndSwap(status_Removing, status_Removed) { 499 badTimer() 500 } 501 changedHeap = true 502 continue nextTimer 503 } 504 case status_Modifying: 505 // Loop until modification complete. 506 scheduler.Yield(scheduler.Thread_WaitReason_Preempted) 507 case status_Unset, status_Removed: 508 // We should not see these status values in a timer heap. 509 badTimer() 510 case status_Running, status_Removing, status_Moving: 511 // Some other P thinks it owns this timer, 512 // which should not happen. 513 badTimer() 514 default: 515 badTimer() 516 } 517 } 518 } 519 520 // Set remaining slots in timers slice to nil, 521 // so that the timer values can be garbage collected. 522 for i := to; i < len(timers); i++ { 523 timers[i].timer = nil 524 } 525 526 th.deletedTimers.Add(-cdel) 527 th.numTimers.Add(-cdel) 528 529 timers = timers[:to] 530 th.timers = timers 531 th.updateTimer0When() 532 533 if verifyTimers { 534 th.verifyTimerHeap() 535 } 536 } 537 538 // verifyTimerHeap verifies that the timer heap is in a valid state. 539 // This is only for debugging, and is only called if verifyTimers is true. 540 // The caller must have locked the th.timersLock 541 func (th *TimingHeap) verifyTimerHeap() { 542 var timers = th.timers 543 var timersLen = len(timers) 544 // First timer has no parent, so i must be start from 1. 545 for i := 1; i < timersLen; i++ { 546 var p = (i - 1) / heapAry 547 if timers[i].when < timers[p].when { 548 print("timer: bad timer heap at ", i, ": ", p, ": ", th.timers[p].when, ", ", i, ": ", timers[i].when, "\n") 549 panic("timer: bad timer heap") 550 } 551 } 552 var numTimers = int(th.numTimers.Load()) 553 if timersLen != numTimers { 554 println("timer: heap len", len(th.timers), "!= numTimers", numTimers) 555 panic("timer: bad timer heap len") 556 } 557 } 558 559 // updateTimer0When sets the timer0When field by check first timer in queue. 560 // The caller must have locked the th.timersLock 561 func (th *TimingHeap) updateTimer0When() { 562 if len(th.timers) == 0 { 563 th.timer0When.Store(0) 564 } else { 565 th.timer0When.Store(th.timers[0].when) 566 } 567 } 568 569 // updateTimerModifiedEarliest updates the th.timerModifiedEarliest value. 570 // The timers will not be locked. 571 func (th *TimingHeap) updateTimerModifiedEarliest(nextWhen monotonic.Time) { 572 for { 573 var old = th.timerModifiedEarliest.Load() 574 if old != 0 && old < nextWhen { 575 return 576 } 577 if th.timerModifiedEarliest.CompareAndSwap(old, nextWhen) { 578 return 579 } 580 } 581 } 582 583 // sleepUntil returns the time when the next timer should fire. 584 func (th *TimingHeap) sleepUntil() (until monotonic.Time) { 585 until = maxWhen 586 587 var timer0When = th.timer0When.Load() 588 if timer0When != 0 && timer0When < until { 589 until = timer0When 590 } 591 592 timer0When = th.timerModifiedEarliest.Load() 593 if timer0When != 0 && timer0When < until { 594 until = timer0When 595 } 596 return 597 } 598 599 // noBarrierWakeTime looks at timers and returns the time when we should wake up. 600 // This function is invoked when dropping a Timers, and must run without any write barriers. 601 // Unlike th.sleepUntil(), It returns 0 if there are no timers. 602 func (th *TimingHeap) noBarrierWakeTime() (until monotonic.Time) { 603 until = th.timer0When.Load() 604 var nextAdj = th.timerModifiedEarliest.Load() 605 if until == 0 || (nextAdj != 0 && nextAdj < until) { 606 until = nextAdj 607 } 608 return 609 } 610 611 // checkTimers runs any timers that are ready. 612 // returns the time when the next timer should run (always larger than the now) or 0 if there is no next timer, 613 // and reports whether it ran any timers. 614 // We pass now in and out to avoid extra calls of monotonic.Now(). 615 func (th *TimingHeap) checkTimers(now monotonic.Time) (nextWhen monotonic.Time, ran bool) { 616 // If it's not yet time for the first timer, or the first adjusted 617 // timer, then there is nothing to do. 618 var next = th.noBarrierWakeTime() 619 if next == 0 { 620 // No timers to run or adjust. 621 return 0, false 622 } 623 624 if now < next { 625 // Next timer is not ready to run, but keep going 626 // if we would clear deleted timers. 627 // This corresponds to the condition below where 628 // we decide whether to call clearDeletedTimers. 629 if th.deletedTimers.Load() <= th.numTimers.Load()/4 { 630 return next, false 631 } 632 } 633 634 th.timersLock.Lock() 635 636 if len(th.timers) > 0 { 637 th.adjustTimers(now) 638 for len(th.timers) > 0 { 639 // Note that th.runTimer may temporarily unlock th.timersLock. 640 var tw = th.runTimer(now) 641 if tw != 0 { 642 if tw > 0 { 643 nextWhen = tw 644 } 645 break 646 } 647 ran = true 648 } 649 } 650 651 // If there are a lot of deleted timers (>25%), clear them out. 652 if int(th.deletedTimers.Load()) > len(th.timers)/4 { 653 th.clearDeletedTimers() 654 } 655 656 th.timersLock.Unlock() 657 return 658 } 659 660 // Check for deadlock situation 661 func (th *TimingHeap) checkDead() { 662 // Maybe jump time forward for playground. 663 // if faketime != 0 { 664 // var when = th.sleepUntil() 665 666 // faketime = when 667 668 // var mp = mget() 669 // if mp == nil { 670 // // There should always be a free M since 671 // // nothing is running. 672 // panic("timers - checkDead: no m for timer") 673 // } 674 // return 675 // } 676 677 // There are no goroutines running, so we can look at the P's. 678 if len(th.timers) > 0 { 679 return 680 } 681 } 682 683 // Heap maintenance algorithms. 684 // These algorithms check for slice index errors manually. 685 // Slice index error can happen if the program is using racy 686 // access to timers. We don't want to panic here, because 687 // it will cause the program to crash with a mysterious 688 // "panic holding locks" message. Instead, we panic while not 689 // holding a lock. 690 691 // siftUpTimer puts the timer at position i in the right place 692 // in the heap by moving it up toward the top of the heap. 693 // It returns the smallest changed index. 694 func (th *TimingHeap) siftUpTimer(i int) int { 695 var timers = th.timers 696 var timerWhen = timers[i].when 697 698 var tmp = timers[i] 699 for i > 0 { 700 var p = (i - 1) / heapAry // parent 701 if timerWhen >= timers[p].when { 702 break 703 } 704 timers[i] = timers[p] 705 i = p 706 } 707 if tmp != timers[i] { 708 timers[i] = tmp 709 } 710 return i 711 } 712 713 // siftDownTimer puts the timer at position i in the right place 714 // in the heap by moving it down toward the bottom of the heap. 715 func (th *TimingHeap) siftDownTimer(i int) { 716 var timers = th.timers 717 var timersLen = len(timers) 718 var timerWhen = timers[i].when 719 720 var tmp = timers[i] 721 for { 722 var c = i*heapAry + 1 // left child 723 var c3 = c + (heapAry / 2) // mid child 724 if c >= timersLen { 725 break 726 } 727 var w = timers[c].when 728 if c+1 < timersLen && timers[c+1].when < w { 729 w = timers[c+1].when 730 c++ 731 } 732 if c3 < timersLen { 733 var w3 = timers[c3].when 734 if c3+1 < timersLen && timers[c3+1].when < w3 { 735 w3 = timers[c3+1].when 736 c3++ 737 } 738 if w3 < w { 739 w = w3 740 c = c3 741 } 742 } 743 if w >= timerWhen { 744 break 745 } 746 timers[i] = timers[c] 747 i = c 748 } 749 if tmp != timers[i] { 750 timers[i] = tmp 751 } 752 }