-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtimer_entries.go
182 lines (162 loc) · 4.01 KB
/
timer_entries.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package gentity
import (
"sort"
"time"
)
// 计时管理
// 参考https://github.com/robfig/cron
// 与cron主要用于任务计划不同,TimerEntries主要用于倒计时的管理
// 如果放在玩家的独立协程中使用,则倒计时的回调可以保证协程安全,使玩家的倒计时回调更简单
//
// example:
//
// go func() {
// defer timerEntries.Stop()
// timerEntries := NewTimerEntries()
// timerEntries.Start()
// for {
// select {
// case timeNow := <-timerEntries.TimerChan():
// timerEntries.Run(timeNow)
// case ...
// }
// }
// }
type TimerEntries struct {
entries []*timerEntry
Timer *time.Timer
// 获取当前时间的接口,默认使用time.Now()
nowFunc func() time.Time
// resetTime时的最小间隔,默认1秒
minInterval time.Duration
// 时间偏差
timeOffset time.Duration
}
func NewTimerEntries() *TimerEntries {
return &TimerEntries{
minInterval: time.Second,
}
}
func NewTimerEntriesWithArgs(nowFunc func() time.Time, minInterval time.Duration) *TimerEntries {
return &TimerEntries{
nowFunc: nowFunc,
minInterval: minInterval,
}
}
// 倒计时回调函数
// 返回值:下一次执行的时间间隔,返回0表示该回调不会继续执行
type TimerJob func() time.Duration
// 时间和回调函数
// 参考https://github.com/robfig/cron
type timerEntry struct {
next time.Time
job TimerJob
}
func (this *TimerEntries) GetMinInterval() time.Duration {
return this.minInterval
}
func (this *TimerEntries) SetMinInterval(minInterval time.Duration) {
this.minInterval = minInterval
}
func (this *TimerEntries) GetTimeOffset() time.Duration {
return this.timeOffset
}
func (this *TimerEntries) SetTimeOffset(timeOffset time.Duration) {
this.timeOffset = timeOffset
}
// 指定时间点执行回调
func (this *TimerEntries) AddTimer(t time.Time, f TimerJob) {
this.addEntry(&timerEntry{t, f})
}
// 现在往后多少时间执行回调
func (this *TimerEntries) After(d time.Duration, f TimerJob) {
this.addEntry(&timerEntry{this.Now().Add(d), f})
}
func (this *TimerEntries) addEntry(entry *timerEntry) {
this.entries = append(this.entries, entry)
this.sort()
if this.Timer == nil {
return
}
this.resetTime(this.Now())
}
func (this *TimerEntries) Now() time.Time {
if this.nowFunc == nil {
if this.timeOffset == 0 {
return time.Now()
} else {
return time.Now().Add(this.timeOffset)
}
}
return this.nowFunc()
}
func (this *TimerEntries) sort() {
sort.Slice(this.entries, func(i, j int) bool {
return this.entries[i].next.Before(this.entries[j].next)
})
}
func (this *TimerEntries) resetTime(now time.Time) {
if len(this.entries) == 0 {
this.Timer.Reset(time.Hour * 100000)
} else {
d := this.entries[0].next.Sub(now)
if d < this.minInterval {
d = this.minInterval
}
this.Timer.Reset(d)
}
}
func (this *TimerEntries) Start() {
this.sort()
if len(this.entries) == 0 {
this.Timer = time.NewTimer(time.Hour * 100000)
} else {
// 以最快到期的时间间隔创建一个NewTimer
this.Timer = time.NewTimer(this.entries[0].next.Sub(this.Now()))
}
}
func (this *TimerEntries) Stop() {
if this.Timer != nil {
this.Timer.Stop()
}
}
func (this *TimerEntries) TimerChan() <-chan time.Time {
return this.Timer.C
}
func (this *TimerEntries) Run(now time.Time) bool {
removed := false
modified := false
jobRun := false
entryCount := len(this.entries)
for i := 0; i < entryCount; i++ {
entry := this.entries[i]
if entry.next.After(now) {
break
}
// job()里面可能执行append(entries,...)
// 新加的entry下次Run才能被执行
d := entry.job()
jobRun = true
if d > 0 {
entry.next = now.Add(d)
} else {
entry.next = time.Time{} // Zero
removed = true
}
modified = true
}
if removed {
// 删除过期的timer
for i := len(this.entries) - 1; i >= 0; i-- {
if this.entries[i].next.IsZero() {
this.entries = append(this.entries[:i], this.entries[i+1:]...)
}
}
}
// 重新排序,重置timer
if modified {
this.sort()
this.resetTime(now)
}
return jobRun
}