github.com/yandex/pandora@v0.5.32/core/coreutil/waiter.go (about) 1 package coreutil 2 3 import ( 4 "context" 5 "time" 6 7 "github.com/yandex/pandora/core" 8 ) 9 10 const MaxOverdueDuration = 2 * time.Second 11 12 // Waiter goroutine unsafe wrapper for efficient waiting schedule. 13 type Waiter struct { 14 sched core.Schedule 15 overdueDuration time.Duration 16 17 // Lazy initialized. 18 timer *time.Timer 19 lastNow time.Time 20 } 21 22 func NewWaiter(sched core.Schedule) *Waiter { 23 return &Waiter{sched: sched} 24 } 25 26 // Wait waits for next waiter schedule event. 27 // Returns true, if event successfully waited, or false 28 // if waiter context is done, or schedule finished. 29 func (w *Waiter) Wait(ctx context.Context) (ok bool) { 30 // Check, that context is not done. Very quick: 5 ns for op, due to benchmark. 31 select { 32 case <-ctx.Done(): 33 w.overdueDuration = 0 34 return false 35 default: 36 } 37 next, ok := w.sched.Next() 38 if !ok { 39 w.overdueDuration = 0 40 return false 41 } 42 // Get current time lazily. 43 // For once schedule, for example, we need to get it only once. 44 waitFor := next.Sub(w.lastNow) 45 if waitFor <= 0 { 46 w.overdueDuration = 0 - waitFor 47 return true 48 } 49 w.lastNow = time.Now() 50 waitFor = next.Sub(w.lastNow) 51 if waitFor <= 0 { 52 w.overdueDuration = 0 - waitFor 53 return true 54 } 55 w.overdueDuration = 0 56 // Lazy init. We don't need timer for unlimited and once schedule. 57 if w.timer == nil { 58 w.timer = time.NewTimer(waitFor) 59 } else { 60 w.timer.Reset(waitFor) 61 } 62 select { 63 case <-w.timer.C: 64 return true 65 case <-ctx.Done(): 66 return false 67 } 68 } 69 70 // IsSlowDown returns true, if schedule contains 2 elements before current time. 71 func (w *Waiter) IsSlowDown(ctx context.Context) (ok bool) { 72 select { 73 case <-ctx.Done(): 74 return false 75 default: 76 return w.overdueDuration >= MaxOverdueDuration 77 } 78 } 79 80 // IsFinished is quick check, that wait context is not canceled and there are some tokens left in 81 // schedule. 82 func (w *Waiter) IsFinished(ctx context.Context) (ok bool) { 83 select { 84 case <-ctx.Done(): 85 return true 86 default: 87 return w.sched.Left() == 0 88 } 89 }