summaryrefslogtreecommitdiffstats
path: root/src/go/pkg/ticker
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-11-25 17:33:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-11-25 17:34:10 +0000
commit83ba6762cc43d9db581b979bb5e3445669e46cc2 (patch)
tree2e69833b43f791ed253a7a20318b767ebe56cdb8 /src/go/pkg/ticker
parentReleasing debian version 1.47.5-1. (diff)
downloadnetdata-83ba6762cc43d9db581b979bb5e3445669e46cc2.tar.xz
netdata-83ba6762cc43d9db581b979bb5e3445669e46cc2.zip
Merging upstream version 2.0.3+dfsg (Closes: #923993, #1042533, #1045145).
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/go/pkg/ticker')
-rw-r--r--src/go/pkg/ticker/ticker.go55
-rw-r--r--src/go/pkg/ticker/ticket_test.go50
2 files changed, 105 insertions, 0 deletions
diff --git a/src/go/pkg/ticker/ticker.go b/src/go/pkg/ticker/ticker.go
new file mode 100644
index 000000000..e4228fe4c
--- /dev/null
+++ b/src/go/pkg/ticker/ticker.go
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package ticker
+
+import "time"
+
+type (
+ // Ticker holds a channel that delivers ticks of a clock at intervals.
+ // The ticks are aligned to interval boundaries.
+ Ticker struct {
+ C <-chan int
+ done chan struct{}
+ loops int
+ interval time.Duration
+ }
+)
+
+// New returns a new Ticker containing a channel that will send the time with a period specified by the duration argument.
+// It adjusts the intervals or drops ticks to make up for slow receivers.
+// The duration must be greater than zero; if not, New will panic. Stop the Ticker to release associated resources.
+func New(interval time.Duration) *Ticker {
+ ticker := &Ticker{
+ interval: interval,
+ done: make(chan struct{}, 1),
+ }
+ ticker.start()
+ return ticker
+}
+
+func (t *Ticker) start() {
+ ch := make(chan int)
+ t.C = ch
+ go func() {
+ LOOP:
+ for {
+ now := time.Now()
+ nextRun := now.Truncate(t.interval).Add(t.interval)
+
+ time.Sleep(nextRun.Sub(now))
+ select {
+ case <-t.done:
+ close(ch)
+ break LOOP
+ case ch <- t.loops:
+ t.loops++
+ }
+ }
+ }()
+}
+
+// Stop turns off a Ticker. After Stop, no more ticks will be sent.
+// Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.
+func (t *Ticker) Stop() {
+ t.done <- struct{}{}
+}
diff --git a/src/go/pkg/ticker/ticket_test.go b/src/go/pkg/ticker/ticket_test.go
new file mode 100644
index 000000000..193085365
--- /dev/null
+++ b/src/go/pkg/ticker/ticket_test.go
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package ticker
+
+import (
+ "testing"
+ "time"
+)
+
+// TODO: often fails Circle CI (~200-240)
+var allowedDelta = 500 * time.Millisecond
+
+func TestTickerParallel(t *testing.T) {
+ for i := 0; i < 100; i++ {
+ i := i
+ go func() {
+ time.Sleep(time.Second / 100 * time.Duration(i))
+ TestTicker(t)
+ }()
+ }
+ time.Sleep(4 * time.Second)
+}
+
+func TestTicker(t *testing.T) {
+ tk := New(time.Second)
+ defer tk.Stop()
+ prev := time.Now()
+ for i := 0; i < 3; i++ {
+ <-tk.C
+ now := time.Now()
+ diff := abs(now.Round(time.Second).Sub(now))
+ if diff >= allowedDelta {
+ t.Errorf("Ticker is not aligned: expect delta < %v but was: %v (%s)", allowedDelta, diff, now.Format(time.RFC3339Nano))
+ }
+ if i > 0 {
+ dt := now.Sub(prev)
+ if abs(dt-time.Second) >= allowedDelta {
+ t.Errorf("Ticker interval: expect delta < %v ns but was: %v", allowedDelta, abs(dt-time.Second))
+ }
+ }
+ prev = now
+ }
+}
+
+func abs(a time.Duration) time.Duration {
+ if a < 0 {
+ return -a
+ }
+ return a
+}