-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathticker.go
145 lines (132 loc) · 4.26 KB
/
ticker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Tideland Go Wait
//
// Copyright (C) 2019-2023 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.
package wait // import "tideland.dev/go/wait"
//--------------------
// IMPORTS
//--------------------
import (
"context"
"math/rand"
"time"
)
//--------------------
// TICKER
//--------------------
// TickerFunc defines a function sending signals for each condition
// check when polling. The ticker can be canceled via the given
// context. Closing the returned signal channel means that the
// ticker ended. Sending ticks should be able to handle not
// received ones in case the condition check of the poller is
// working.
type TickerFunc func(ctx context.Context) <-chan struct{}
// TickChangerFunc allows to work with changing intervals. The
// current one is the argument, the next has to be returned. In
// case the bool return value is false the ticker will stop.
type TickChangerFunc func(in time.Duration) (out time.Duration, ok bool)
// MakeGenericIntervalTicker is a factory for tickers based on time
// intervals. The given changer is responsible for the intervals and
// if the ticker shall signal a stopping. The changer is called initially
// with a duration of zero to allow the changer stopping the ticker even
// before a first tick.
func MakeGenericIntervalTicker(changer TickChangerFunc) TickerFunc {
return func(ctx context.Context) <-chan struct{} {
tickc := make(chan struct{})
interval := 0 * time.Millisecond
ok := true
go func() {
defer close(tickc)
// Defensive changer call.
if interval, ok = changer(interval); !ok {
return
}
// TickerFunc for the interval.
timer := time.NewTimer(interval)
defer timer.Stop()
// Loop sending signals.
for {
select {
case <-timer.C:
// One interval tick. Ignore if needed.
select {
case tickc <- struct{}{}:
default:
}
case <-ctx.Done():
// Given context stopped.
return
}
// Reset timer with next interval.
if interval, ok = changer(interval); !ok {
return
}
timer.Reset(interval)
}
}()
return tickc
}
}
// MakeIntervalTicker returns a ticker signalling in intervals.
func MakeIntervalTicker(interval time.Duration) TickerFunc {
changer := func(_ time.Duration) (out time.Duration, ok bool) {
return interval, true
}
return MakeGenericIntervalTicker(changer)
}
// MakeMaxIntervalsTicker returns a ticker signalling in intervals. It
// stops after a maximum number of signals.
func MakeMaxIntervalsTicker(interval time.Duration, max int) TickerFunc {
count := 0
changer := func(_ time.Duration) (out time.Duration, ok bool) {
count++
if count > max {
return 0, false
}
return interval, true
}
return MakeGenericIntervalTicker(changer)
}
// MakeDeadlinedIntervalTicker returns a ticker signalling in intervals
// and stopping after a deadline.
func MakeDeadlinedIntervalTicker(interval time.Duration, deadline time.Time) TickerFunc {
changer := func(_ time.Duration) (out time.Duration, ok bool) {
if time.Now().After(deadline) {
return 0, false
}
return interval, true
}
return MakeGenericIntervalTicker(changer)
}
// MakeExpiringIntervalTicker returns a ticker signalling in intervals
// and stopping after a timeout.
func MakeExpiringIntervalTicker(interval, timeout time.Duration) TickerFunc {
deadline := time.Now().Add(timeout)
changer := func(_ time.Duration) (out time.Duration, ok bool) {
if time.Now().After(deadline) {
return 0, false
}
return interval, true
}
return MakeGenericIntervalTicker(changer)
}
// MakeJitteringTicker returns a ticker signalling in jittering intervals. This
// avoids converging on periadoc behavior during condition check. The returned
// intervals jitter between the given interval and interval + factor * interval.
// The ticker stops after reaching timeout.
func MakeJitteringTicker(interval time.Duration, factor float64, timeout time.Duration) TickerFunc {
deadline := time.Now().Add(timeout)
changer := func(_ time.Duration) (time.Duration, bool) {
if time.Now().After(deadline) {
return 0, false
}
if factor <= 0.0 {
factor = 1.0
}
return interval + time.Duration(rand.Float64()*factor*float64(interval)), true
}
return MakeGenericIntervalTicker(changer)
}
// EOF