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  }