mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
gcron, gtimer updates
This commit is contained in:
2
TODO.MD
2
TODO.MD
@ -46,7 +46,7 @@
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. 增加jumplist的数据结构容器;
|
||||
1. DelayQueue/PriorityQueue;
|
||||
|
||||
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag;
|
||||
|
||||
# DONE
|
||||
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
|
||||
|
||||
@ -81,6 +81,7 @@ func (entry *Entry) Stop() {
|
||||
|
||||
// 关闭定时任务
|
||||
func (entry *Entry) Close() {
|
||||
entry.cron.Remove(entry.Name)
|
||||
entry.entry.Close()
|
||||
}
|
||||
|
||||
@ -92,6 +93,7 @@ func (entry *Entry) check() {
|
||||
return
|
||||
|
||||
case STATUS_CLOSED:
|
||||
entry.cron.Remove(entry.Name)
|
||||
gtimer.Exit()
|
||||
|
||||
case STATUS_READY: fallthrough
|
||||
|
||||
@ -72,6 +72,7 @@ func TestCron_Basic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCron_AddSingleton(t *testing.T) {
|
||||
// un used, can be removed
|
||||
gtest.Case(t, func() {
|
||||
cron := gcron.New()
|
||||
cron.Add("* * * * * *", func() {}, "add")
|
||||
@ -88,7 +89,7 @@ func TestCron_AddSingleton(t *testing.T) {
|
||||
gtest.AssertNE(entry1, nil)
|
||||
gtest.Assert(entry2, nil)
|
||||
})
|
||||
|
||||
// keep this
|
||||
gtest.Case(t, func() {
|
||||
cron := gcron.New()
|
||||
array := garray.New(0, 0)
|
||||
@ -193,7 +194,7 @@ func TestCron_DelayAddTimes(t *testing.T) {
|
||||
time.Sleep(800*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 0)
|
||||
gtest.Assert(cron.Size(), 1)
|
||||
time.Sleep(5000*time.Millisecond)
|
||||
time.Sleep(3000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
gtest.Assert(cron.Size(), 0)
|
||||
})
|
||||
|
||||
@ -10,6 +10,7 @@ package gcron_test
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/garray"
|
||||
"gitee.com/johng/gf/g/os/gcron"
|
||||
"gitee.com/johng/gf/g/os/glog"
|
||||
"gitee.com/johng/gf/g/util/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
@ -17,9 +18,26 @@ import (
|
||||
|
||||
func TestCron_Entry_Operations(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
|
||||
gtest.Case(t, func() {
|
||||
cron := gcron.New()
|
||||
array := garray.New(0, 0)
|
||||
cron.DelayAddTimes(500*time.Millisecond, "* * * * * *", 2, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
gtest.Assert(cron.Size(), 0)
|
||||
time.Sleep(800*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 0)
|
||||
gtest.Assert(cron.Size(), 1)
|
||||
time.Sleep(3000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
gtest.Assert(cron.Size(), 0)
|
||||
})
|
||||
|
||||
cron := gcron.New()
|
||||
array := garray.New(0, 0)
|
||||
entry, err1 := cron.Add("* * * * * *", func() {
|
||||
glog.Println("add")
|
||||
array.Append(1)
|
||||
})
|
||||
gtest.Assert(err1, nil)
|
||||
@ -29,16 +47,16 @@ func TestCron_Entry_Operations(t *testing.T) {
|
||||
gtest.Assert(array.Len(), 1)
|
||||
gtest.Assert(cron.Size(), 1)
|
||||
entry.Stop()
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
time.Sleep(2000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
gtest.Assert(cron.Size(), 1)
|
||||
entry.Start()
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
glog.Println("start")
|
||||
time.Sleep(1000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
gtest.Assert(cron.Size(), 1)
|
||||
entry.Close()
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(cron.Size(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -4,10 +4,10 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// Package gtimer implements Levelled Timing Wheel for interval/delayed jobs running and management.
|
||||
// Package gtimer implements Hierarchical Timing Wheel for interval/delayed jobs running and management.
|
||||
//
|
||||
// 任务定时器(分层时间轮),
|
||||
// 高效的时间轮任务管理模块,用于管理间隔/延迟运行任务。
|
||||
// 任务定时器,
|
||||
// 高性能的分层时间轮任务管理模块,用于管理间隔/延迟运行任务。
|
||||
// 与gcron模块的区别是,时间轮模块只管理间隔执行任务,并且更注重执行效率(纳秒级别)。
|
||||
// 需要注意执行时间间隔的准确性问题: https://github.com/golang/go/issues/14410
|
||||
package gtimer
|
||||
@ -30,47 +30,46 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// 默认的wheel管理对象
|
||||
// 默认的wheel管理对象。
|
||||
defaultTimer = New(gDEFAULT_SLOT_NUMBER, gDEFAULT_WHEEL_INTERVAL, gDEFAULT_WHEEL_LEVEL)
|
||||
)
|
||||
|
||||
// 类似与js中的SetTimeout,一段时间后执行回调函数
|
||||
// 类似与js中的SetTimeout,一段时间后执行回调函数。
|
||||
func SetTimeout(delay time.Duration, job JobFunc) {
|
||||
AddOnce(delay, job)
|
||||
}
|
||||
|
||||
// 类似与js中的SetInterval,每隔一段时间后执行回调函数,当回调函数返回true,那么继续执行,否则终止执行,该方法是异步的
|
||||
// 注意:由于采用的是循环而不是递归操作,因此间隔时间将会以上一次回调函数执行完成的时间来计算
|
||||
// 类似与js中的SetInterval,每隔一段时间执行指定回调函数。
|
||||
func SetInterval(interval time.Duration, job JobFunc) {
|
||||
Add(interval, job)
|
||||
}
|
||||
|
||||
// 添加执行方法,可以给定名字,以便于后续执行删除
|
||||
// 添加执行方法。
|
||||
func Add(interval time.Duration, job JobFunc) *Entry {
|
||||
return defaultTimer.Add(interval, job)
|
||||
}
|
||||
|
||||
// 添加执行方法,可以给定名字,以便于后续执行删除
|
||||
// 添加执行方法,更多参数控制。
|
||||
func AddEntry(interval time.Duration, job JobFunc, singleton bool, times int) *Entry {
|
||||
return defaultTimer.AddEntry(interval, job, singleton, times)
|
||||
}
|
||||
|
||||
// 添加单例运行循环任务
|
||||
// 添加单例运行循环任务。
|
||||
func AddSingleton(interval time.Duration, job JobFunc) *Entry {
|
||||
return defaultTimer.AddSingleton(interval, job)
|
||||
}
|
||||
|
||||
// 添加只运行一次的循环任务
|
||||
// 添加只运行一次的循环任务。
|
||||
func AddOnce(interval time.Duration, job JobFunc) *Entry {
|
||||
return defaultTimer.AddOnce(interval, job)
|
||||
}
|
||||
|
||||
// 添加运行指定次数的循环任务
|
||||
// 添加运行指定次数的循环任务。
|
||||
func AddTimes(interval time.Duration, times int, job JobFunc) *Entry {
|
||||
return defaultTimer.AddTimes(interval, times, job)
|
||||
}
|
||||
|
||||
// 延迟添加循环任务,delay参数单位为秒
|
||||
// 延迟添加循环任务。
|
||||
func DelayAdd(delay time.Duration, interval time.Duration, job JobFunc) {
|
||||
defaultTimer.DelayAdd(delay, interval, job)
|
||||
}
|
||||
@ -95,7 +94,7 @@ func DelayAddTimes(delay time.Duration, interval time.Duration, times int, job J
|
||||
defaultTimer.DelayAddTimes(delay, interval, times, job)
|
||||
}
|
||||
|
||||
// 在Job方法中调用,停止当前运行的任务
|
||||
// 在Job方法中调用,停止当前运行的任务。
|
||||
func Exit() {
|
||||
panic(gPANIC_EXIT)
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ type Entry struct {
|
||||
// 任务执行方法
|
||||
type JobFunc = func()
|
||||
|
||||
// 创建定时任务
|
||||
// 创建定时任务。
|
||||
func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, times int) *Entry {
|
||||
ms := interval.Nanoseconds()/1e6
|
||||
num := ms/w.intervalMs
|
||||
@ -56,10 +56,9 @@ func (w *wheel) addEntry(interval time.Duration, job JobFunc, singleton bool, ti
|
||||
return entry
|
||||
}
|
||||
|
||||
// 创建定时任务
|
||||
func (w *wheel) addEntryByParent(interval time.Duration, parent *Entry) *Entry {
|
||||
ms := interval.Nanoseconds()/1e6
|
||||
num := ms/w.intervalMs
|
||||
// 创建定时任务,给定父级Entry, 间隔参数参数为毫秒数.
|
||||
func (w *wheel) addEntryByParent(interval int64, parent *Entry) *Entry {
|
||||
num := interval/w.intervalMs
|
||||
if num == 0 {
|
||||
num = 1
|
||||
}
|
||||
@ -74,7 +73,7 @@ func (w *wheel) addEntryByParent(interval time.Duration, parent *Entry) *Entry {
|
||||
interval : num,
|
||||
singleton : parent.singleton,
|
||||
createMs : nowMs,
|
||||
intervalMs : ms,
|
||||
intervalMs : interval,
|
||||
rawIntervalMs : parent.rawIntervalMs,
|
||||
}
|
||||
w.slots[(ticks + num) % w.number].PushBack(entry)
|
||||
@ -126,21 +125,20 @@ func (entry *Entry) Run() {
|
||||
entry.job()
|
||||
}
|
||||
|
||||
// 检测当前任务是否可运行, 参数为当前时间的纳秒数, 精度更高
|
||||
// 检测当前任务是否可运行。
|
||||
func (entry *Entry) check(nowTicks int64, nowMs int64) bool {
|
||||
switch entry.status.Val() {
|
||||
case STATUS_STOPPED: fallthrough
|
||||
case STATUS_CLOSED:
|
||||
return false
|
||||
}
|
||||
// 时间轮客户端判断,是否满足运行刻度条件,误差会比较大
|
||||
if diff := nowTicks - entry.create; diff > 0 && diff%entry.interval == 0 {
|
||||
// 分层转换处理
|
||||
if entry.wheel.level > 0 {
|
||||
diffMs := nowMs - entry.createMs
|
||||
|
||||
//fmt.Println("diffMs:", entry.wheel.level, diffMs, entry.intervalMs, entry.rawIntervalMs)
|
||||
switch {
|
||||
// 表示新增
|
||||
// 表示新增(当添加任务后在下一时间轮刻度马上触发)
|
||||
case diffMs < entry.wheel.timer.intervalMs:
|
||||
entry.wheel.slots[(nowTicks+entry.interval)%entry.wheel.number].PushBack(entry)
|
||||
return false
|
||||
@ -149,23 +147,15 @@ func (entry *Entry) check(nowTicks int64, nowMs int64) bool {
|
||||
case diffMs >= entry.wheel.timer.intervalMs:
|
||||
// 任务是否有必要进行分层转换
|
||||
if leftMs := entry.intervalMs - diffMs; leftMs > entry.wheel.timer.intervalMs {
|
||||
//fmt.Println("leveled",
|
||||
// entry.wheel.level,
|
||||
// entry.wheel.ticks.Val(),
|
||||
// entry.create,
|
||||
// diffMs,
|
||||
// leftMs,
|
||||
// entry.rawIntervalMs,
|
||||
//)
|
||||
// 往底层添加
|
||||
entry.wheel.timer.doAddEntryByParent(time.Duration(leftMs)*time.Millisecond, entry)
|
||||
// 往底层添加,通过毫秒计算并重新添加任务到对应的时间轮上,减小运行误差
|
||||
entry.wheel.timer.doAddEntryByParent(leftMs, entry)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// 是否单例
|
||||
if entry.IsSingleton() {
|
||||
//fmt.Println("IsSingleton", entry.status.Val(), entry.intervalMs)
|
||||
// 注意原子操作结果判断
|
||||
if entry.status.Set(STATUS_RUNNING) == STATUS_RUNNING {
|
||||
return false
|
||||
}
|
||||
@ -173,7 +163,7 @@ func (entry *Entry) check(nowTicks int64, nowMs int64) bool {
|
||||
// 次数限制
|
||||
times := entry.times.Add(-1)
|
||||
if times <= 0 {
|
||||
// 注意原子操作
|
||||
// 注意原子操作结果判断
|
||||
if entry.status.Set(STATUS_CLOSED) == STATUS_CLOSED || times < 0 {
|
||||
return false
|
||||
}
|
||||
@ -183,7 +173,6 @@ func (entry *Entry) check(nowTicks int64, nowMs int64) bool {
|
||||
times = gDEFAULT_TIMES
|
||||
entry.times.Set(gDEFAULT_TIMES)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@ -38,12 +38,7 @@ func (w *wheel) proceed() {
|
||||
n := w.ticks.Add(1)
|
||||
l := w.slots[int(n%w.number)]
|
||||
length := l.Len()
|
||||
//if w.level > 0 {
|
||||
// fmt.Println("loop:", w.level, w.ticks.Val(), time.Now().String())
|
||||
//}
|
||||
|
||||
if length > 0 {
|
||||
|
||||
go func(l *glist.List, nowTicks int64) {
|
||||
entry := (*Entry)(nil)
|
||||
nowMs := time.Now().UnixNano()/1e6
|
||||
@ -53,10 +48,6 @@ func (w *wheel) proceed() {
|
||||
} else {
|
||||
entry = v.(*Entry)
|
||||
}
|
||||
//fmt.Println(w.level, w.ticks.Val(), entry.create, entry.rawIntervalMs)
|
||||
if entry.Status() == STATUS_CLOSED {
|
||||
continue
|
||||
}
|
||||
// 是否满足运行条件
|
||||
if entry.check(nowTicks, nowMs) {
|
||||
// 异步执行运行
|
||||
@ -76,9 +67,9 @@ func (w *wheel) proceed() {
|
||||
entry.job()
|
||||
}(entry)
|
||||
}
|
||||
// 是否继续添运行
|
||||
// 是否继续添运行, 滚动任务
|
||||
if entry.status.Val() != STATUS_CLOSED {
|
||||
entry.wheel.timer.doAddEntryByParent(time.Duration(entry.rawIntervalMs)*time.Millisecond, entry)
|
||||
entry.wheel.timer.doAddEntryByParent(entry.rawIntervalMs, entry)
|
||||
}
|
||||
}
|
||||
}(l, n)
|
||||
|
||||
@ -14,13 +14,25 @@ import (
|
||||
|
||||
// 定时器/分层时间轮
|
||||
type Timer struct {
|
||||
status *gtype.Int // 状态
|
||||
wheels []*wheel // 分层
|
||||
length int // 层数
|
||||
status *gtype.Int // 定时器状态
|
||||
wheels []*wheel // 分层时间轮对象
|
||||
length int // 分层层数
|
||||
number int // 每一层Slot Number
|
||||
intervalMs int64 // 最小时间刻度(毫秒)
|
||||
}
|
||||
|
||||
// 单层时间轮
|
||||
type wheel struct {
|
||||
timer *Timer // 所属定时器
|
||||
level int // 所属分层索引号
|
||||
slots []*glist.List // 所有的循环任务项, 按照Slot Number进行分组
|
||||
number int64 // Slot Number=len(slots)
|
||||
ticks *gtype.Int64 // 当前时间轮已转动的刻度数量
|
||||
totalMs int64 // 整个时间轮的时间长度(毫秒)=number*interval
|
||||
createMs int64 // 创建时间(毫秒)
|
||||
intervalMs int64 // 时间间隔(slot时间长度, 毫秒)
|
||||
}
|
||||
|
||||
// 创建分层时间轮
|
||||
func New(slot int, interval time.Duration, level...int) *Timer {
|
||||
length := gDEFAULT_WHEEL_LEVEL
|
||||
@ -146,16 +158,16 @@ func (t *Timer) doAddEntry(interval time.Duration, job JobFunc, singleton bool,
|
||||
return t.wheels[t.getLevelByIntervalMs(interval.Nanoseconds()/1e6)].addEntry(interval, job, singleton, times)
|
||||
}
|
||||
|
||||
// 添加定时任务
|
||||
func (t *Timer) doAddEntryByParent(interval time.Duration, parent *Entry) *Entry {
|
||||
return t.wheels[t.getLevelByIntervalMs(interval.Nanoseconds()/1e6)].addEntryByParent(interval, parent)
|
||||
// 添加定时任务,给定父级Entry, 间隔参数参数为毫秒数.
|
||||
func (t *Timer) doAddEntryByParent(interval int64, parent *Entry) *Entry {
|
||||
return t.wheels[t.getLevelByIntervalMs(interval)].addEntryByParent(interval, parent)
|
||||
}
|
||||
|
||||
// 根据intervalMs计算添加的分层索引
|
||||
func (t *Timer) getLevelByIntervalMs(intervalMs int64) int {
|
||||
pos, cmp := t.binSearchIndex(intervalMs)
|
||||
switch cmp {
|
||||
// intervalMs与最后匹配值相等
|
||||
// intervalMs与最后匹配值相等, 不添加到匹配得层,而是向下一层添加
|
||||
case 0: fallthrough
|
||||
// intervalMs比最后匹配值小
|
||||
case -1:
|
||||
@ -180,7 +192,7 @@ func (t *Timer) getLevelByIntervalMs(intervalMs int64) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 二分查找当前任务可以添加的时间轮对象索引
|
||||
// 二分查找当前任务可以添加的时间轮对象索引.
|
||||
func (t *Timer) binSearchIndex(n int64)(index int, result int) {
|
||||
min := 0
|
||||
max := t.length - 1
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
package gtimer
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/glist"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
)
|
||||
|
||||
// 单层时间轮
|
||||
type wheel struct {
|
||||
timer *Timer // 所属定时器
|
||||
level int // 所属分层索引号
|
||||
slots []*glist.List // 所有的循环任务项, 按照Slot Number进行分组
|
||||
number int64 // Slot Number
|
||||
ticks *gtype.Int64 // 当前时间轮已转动的刻度数量
|
||||
totalMs int64 // 整个时间轮的时间长度(毫秒)=number*interval
|
||||
createMs int64 // 创建时间(毫秒)
|
||||
intervalMs int64 // 时间间隔(slot时间长度, 毫秒)
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// 包方法操作
|
||||
// Timer Operations
|
||||
|
||||
package gtimer_test
|
||||
|
||||
@ -21,20 +21,42 @@ func New() *gtimer.Timer {
|
||||
return gtimer.New(10, 10*time.Millisecond)
|
||||
}
|
||||
|
||||
func TestSetTimeout(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
array := garray.New(0, 0)
|
||||
gtimer.SetTimeout(200*time.Millisecond, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(1000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetInterval(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
array := garray.New(0, 0)
|
||||
gtimer.SetInterval(200*time.Millisecond, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(1100*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 5)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_Add_Close(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := New()
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
//fmt.Println("start", time.Now())
|
||||
wheel.Add(time.Second, func() {
|
||||
timer.Add(time.Second, func() {
|
||||
//fmt.Println("entry1", time.Now())
|
||||
array.Append(1)
|
||||
})
|
||||
wheel.Add(time.Second, func() {
|
||||
timer.Add(time.Second, func() {
|
||||
//fmt.Println("entry2", time.Now())
|
||||
array.Append(1)
|
||||
})
|
||||
wheel.Add(2*time.Second, func() {
|
||||
timer.Add(2*time.Second, func() {
|
||||
//fmt.Println("entry3", time.Now())
|
||||
array.Append(1)
|
||||
})
|
||||
@ -42,7 +64,7 @@ func TestTimer_Add_Close(t *testing.T) {
|
||||
gtest.Assert(array.Len(), 2)
|
||||
time.Sleep(1300*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 5)
|
||||
wheel.Close()
|
||||
timer.Close()
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
fixedLength := array.Len()
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
@ -50,11 +72,34 @@ func TestTimer_Add_Close(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_Singleton(t *testing.T) {
|
||||
func TestTimer_Start_Stop_Close(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
timer.Add(200*time.Millisecond, func() {
|
||||
//glog.Println("add...")
|
||||
array.Append(1)
|
||||
})
|
||||
gtest.Assert(array.Len(), 0)
|
||||
time.Sleep(300*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
timer.Stop()
|
||||
time.Sleep(1000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
timer.Start()
|
||||
time.Sleep(200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
timer.Close()
|
||||
time.Sleep(1000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_AddSingleton(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := New()
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.AddSingleton(time.Second, func() {
|
||||
timer.AddSingleton(time.Second, func() {
|
||||
array.Append(1)
|
||||
time.Sleep(10*time.Second)
|
||||
})
|
||||
@ -66,21 +111,21 @@ func TestTimer_Singleton(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_Once(t *testing.T) {
|
||||
func TestTimer_AddOnce(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := New()
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.AddOnce(time.Second, func() {
|
||||
timer.AddOnce(time.Second, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
wheel.AddOnce(time.Second, func() {
|
||||
timer.AddOnce(time.Second, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
wheel.Close()
|
||||
timer.Close()
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
fixedLength := array.Len()
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
@ -88,11 +133,23 @@ func TestTimer_Once(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_AddTimes(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
timer.AddTimes(time.Second, 2, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(3500*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_DelayAdd(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := New()
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.DelayAdd(time.Second, time.Second, func() {
|
||||
timer.DelayAdd(time.Second, time.Second, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
@ -102,11 +159,11 @@ func TestTimer_DelayAdd(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_DelayAdd_Singleton(t *testing.T) {
|
||||
func TestTimer_DelayAddSingleton(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := New()
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.DelayAddSingleton(time.Second, time.Second, func() {
|
||||
timer.DelayAddSingleton(time.Second, time.Second, func() {
|
||||
array.Append(1)
|
||||
time.Sleep(10*time.Second)
|
||||
})
|
||||
@ -118,11 +175,11 @@ func TestTimer_DelayAdd_Singleton(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_DelayAdd_Once(t *testing.T) {
|
||||
func TestTimer_DelayAddOnce(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := New()
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.DelayAddOnce(time.Second, time.Second, func() {
|
||||
timer.DelayAddOnce(time.Second, time.Second, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
@ -136,15 +193,36 @@ func TestTimer_DelayAdd_Once(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_ExitJob(t *testing.T) {
|
||||
func TestTimer_DelayAddTimes(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
timer.DelayAddTimes(200*time.Millisecond, 500*time.Millisecond, 2, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 0)
|
||||
|
||||
time.Sleep(600*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
|
||||
time.Sleep(600*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
|
||||
time.Sleep(1000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTimer_Exit(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := New()
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.Add(time.Second, func() {
|
||||
timer.Add(200*time.Millisecond, func() {
|
||||
array.Append(1)
|
||||
gtimer.Exit()
|
||||
})
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
time.Sleep(1000*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -4,21 +4,22 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// Entry操作
|
||||
// Entry Operations
|
||||
|
||||
package gtimer_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/garray"
|
||||
"gitee.com/johng/gf/g/os/gtimer"
|
||||
"gitee.com/johng/gf/g/util/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTimer_Entry_Operation(t *testing.T) {
|
||||
wheel := New()
|
||||
func TestEntry_Start_Stop_Close(t *testing.T) {
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
entry := wheel.Add(time.Second, func() {
|
||||
entry := timer.Add(time.Second, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
@ -32,16 +33,20 @@ func TestTimer_Entry_Operation(t *testing.T) {
|
||||
entry.Close()
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
|
||||
gtest.Assert(entry.Status(), gtimer.STATUS_CLOSED)
|
||||
}
|
||||
|
||||
func TestTimer_Entry_Singleton(t *testing.T) {
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
entry := wheel.Add(time.Second, func() {
|
||||
func TestEntry_Singleton(t *testing.T) {
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
entry := timer.Add(time.Second, func() {
|
||||
array.Append(1)
|
||||
time.Sleep(10*time.Second)
|
||||
})
|
||||
gtest.Assert(entry.IsSingleton(), false)
|
||||
entry.SetSingleton(true)
|
||||
gtest.Assert(entry.IsSingleton(), true)
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
|
||||
@ -49,13 +54,14 @@ func TestTimer_Entry_Singleton(t *testing.T) {
|
||||
gtest.Assert(array.Len(), 1)
|
||||
}
|
||||
|
||||
func TestTimer_Entry_Once(t *testing.T) {
|
||||
wheel := New()
|
||||
func TestEntry_SetTimes(t *testing.T) {
|
||||
timer := New()
|
||||
array := garray.New(0, 0)
|
||||
entry := wheel.Add(time.Second, func() {
|
||||
entry := timer.Add(200*time.Millisecond, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
entry.SetTimes(1)
|
||||
entry.SetTimes(2)
|
||||
time.Sleep(1200*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 1)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
}
|
||||
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
// 指定次数运行测试
|
||||
|
||||
package gtimer_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/garray"
|
||||
"gitee.com/johng/gf/g/util/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTimer_Times(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
wheel := New()
|
||||
array := garray.New(0, 0)
|
||||
wheel.AddTimes(time.Second, 2, func() {
|
||||
array.Append(1)
|
||||
})
|
||||
time.Sleep(3500*time.Millisecond)
|
||||
gtest.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
@ -15,6 +15,8 @@ import (
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -22,8 +24,7 @@ import (
|
||||
func Case(t *testing.T, f func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
glog.To(os.Stderr).Println(err)
|
||||
glog.Header(false).PrintBacktrace(2)
|
||||
fmt.Fprintf(os.Stderr, "%v\n%s", err, getBacktrace())
|
||||
t.Fail()
|
||||
}
|
||||
}()
|
||||
@ -205,4 +206,46 @@ func Fatal(message...interface{}) {
|
||||
glog.To(os.Stderr).Println(`[FATAL]`, fmt.Sprint(message...))
|
||||
glog.Header(false).PrintBacktrace(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 获取文件调用回溯字符串,参数skip表示调用端往上多少级开始回溯
|
||||
func getBacktrace(skip...int) string {
|
||||
customSkip := 0
|
||||
if len(skip) > 0 {
|
||||
customSkip = skip[0]
|
||||
}
|
||||
backtrace := ""
|
||||
index := 1
|
||||
from := 0
|
||||
// 首先定位业务文件开始位置
|
||||
for i := 0; i < 10; i++ {
|
||||
if _, file, _, ok := runtime.Caller(i); ok {
|
||||
if reg, _ := regexp.Compile("/g/util/gtest/.+$"); !reg.MatchString(file) {
|
||||
from = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// 从业务文件开始位置根据自定义的skip开始backtrace
|
||||
goRoot := runtime.GOROOT()
|
||||
for i := from + customSkip; i < 10000; i++ {
|
||||
if _, file, cline, ok := runtime.Caller(i); ok && file != "" {
|
||||
if reg, _ := regexp.Compile(`<autogenerated>`); reg.MatchString(file) {
|
||||
continue
|
||||
}
|
||||
if reg, _ := regexp.Compile("/g/util/gtest/.+$"); reg.MatchString(file) {
|
||||
continue
|
||||
}
|
||||
if goRoot != "" {
|
||||
if reg, _ := regexp.Compile("^" + goRoot); reg.MatchString(file) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, "\n")
|
||||
index++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return backtrace
|
||||
}
|
||||
Reference in New Issue
Block a user