NewVariableTicker wrap time.Ticker to Reset() the ticker with the next duration (picked from input durations) after each tick. The last configured duration is the one that will be preserved once previous ones have been applied. Returns a function for stopping the ticker, and the ticker channel.
(durations ...time.Duration)
| 11 | // |
| 12 | // Returns a function for stopping the ticker, and the ticker channel. |
| 13 | func NewVariableTicker(durations ...time.Duration) (func(), <-chan time.Time) { |
| 14 | if len(durations) == 0 { |
| 15 | panic("at least 1 duration required") |
| 16 | } |
| 17 | |
| 18 | // Init the ticker with the 1st duration. |
| 19 | ticker := time.NewTicker(durations[0]) |
| 20 | durations = durations[1:] |
| 21 | |
| 22 | // If there was only 1 duration we can simply return the built-in ticker. |
| 23 | if len(durations) == 0 { |
| 24 | return ticker.Stop, ticker.C |
| 25 | } |
| 26 | |
| 27 | // Create a channel over which our ticks will be sent. |
| 28 | ticks := make(chan time.Time, 1) |
| 29 | |
| 30 | // Create a channel used to signal once this ticker is stopped. |
| 31 | stopped := make(chan struct{}) |
| 32 | |
| 33 | go func() { |
| 34 | for { |
| 35 | select { |
| 36 | case ts := <-ticker.C: |
| 37 | if len(durations) > 0 { |
| 38 | ticker.Reset(durations[0]) |
| 39 | durations = durations[1:] |
| 40 | } |
| 41 | |
| 42 | // Non-blocking send to avoid goroutine leak if stopped while consumer is slow. |
| 43 | select { |
| 44 | case ticks <- ts: |
| 45 | case <-stopped: |
| 46 | return |
| 47 | } |
| 48 | |
| 49 | case <-stopped: |
| 50 | // Interrupt the loop once stopped. |
| 51 | return |
| 52 | } |
| 53 | } |
| 54 | }() |
| 55 | |
| 56 | stopOnce := sync.Once{} |
| 57 | stop := func() { |
| 58 | stopOnce.Do(func() { |
| 59 | ticker.Stop() |
| 60 | close(stopped) |
| 61 | }) |
| 62 | } |
| 63 | |
| 64 | return stop, ticks |
| 65 | } |