forked from vitessio/vitess
-
Notifications
You must be signed in to change notification settings - Fork 13
/
timer.go
143 lines (122 loc) · 2.96 KB
/
timer.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
// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package timer provides various enhanced timer functions.
package timer
import (
"sync"
"time"
"github.com/youtube/vitess/go/sync2"
)
// Out-of-band messages
type typeAction int
const (
timerStop typeAction = iota
timerReset
timerTrigger
)
/*
Timer provides timer functionality that can be controlled
by the user. You start the timer by providing it a callback function,
which it will call at the specified interval.
var t = timer.NewTimer(1e9)
t.Start(KeepHouse)
func KeepHouse() {
// do house keeping work
}
You can stop the timer by calling t.Stop, which is guaranteed to
wait if KeepHouse is being executed.
You can create an untimely trigger by calling t.Trigger. You can also
schedule an untimely trigger by calling t.TriggerAfter.
The timer interval can be changed on the fly by calling t.SetInterval.
A zero value interval will cause the timer to wait indefinitely, and it
will react only to an explicit Trigger or Stop.
*/
type Timer struct {
interval sync2.AtomicDuration
// state management
mu sync.Mutex
running bool
// msg is used for out-of-band messages
msg chan typeAction
}
// NewTimer creates a new Timer object
func NewTimer(interval time.Duration) *Timer {
tm := &Timer{
msg: make(chan typeAction),
}
tm.interval.Set(interval)
return tm
}
// Start starts the timer.
func (tm *Timer) Start(keephouse func()) {
tm.mu.Lock()
defer tm.mu.Unlock()
if tm.running {
return
}
tm.running = true
go tm.run(keephouse)
}
func (tm *Timer) run(keephouse func()) {
for {
var ch <-chan time.Time
interval := tm.interval.Get()
if interval <= 0 {
ch = nil
} else {
ch = time.After(interval)
}
select {
case action := <-tm.msg:
switch action {
case timerStop:
return
case timerReset:
continue
}
case <-ch:
}
keephouse()
}
}
// SetInterval changes the wait interval.
// It will cause the timer to restart the wait.
func (tm *Timer) SetInterval(ns time.Duration) {
tm.interval.Set(ns)
tm.mu.Lock()
defer tm.mu.Unlock()
if tm.running {
tm.msg <- timerReset
}
}
// Trigger will cause the timer to immediately execute the keephouse function.
// It will then cause the timer to restart the wait.
func (tm *Timer) Trigger() {
tm.mu.Lock()
defer tm.mu.Unlock()
if tm.running {
tm.msg <- timerTrigger
}
}
// TriggerAfter waits for the specified duration and triggers the next event.
func (tm *Timer) TriggerAfter(duration time.Duration) {
go func() {
time.Sleep(duration)
tm.Trigger()
}()
}
// Stop will stop the timer. It guarantees that the timer will not execute
// any more calls to keephouse once it has returned.
func (tm *Timer) Stop() {
tm.mu.Lock()
defer tm.mu.Unlock()
if tm.running {
tm.msg <- timerStop
tm.running = false
}
}
// Interval returns the current interval.
func (tm *Timer) Interval() time.Duration {
return tm.interval.Get()
}