You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
5.1 KiB
268 lines
5.1 KiB
package time |
|
|
|
import ( |
|
"sync" |
|
itime "time" |
|
|
|
"go-common/library/log" |
|
) |
|
|
|
const ( |
|
timerFormat = "2006-01-02 15:04:05" |
|
infiniteDuration = itime.Duration(1<<63 - 1) |
|
) |
|
|
|
var ( |
|
timerLazyDelay = 300 * itime.Millisecond |
|
) |
|
|
|
// TimerData timer data. |
|
type TimerData struct { |
|
Key string |
|
expire itime.Time |
|
fn func() |
|
index int |
|
next *TimerData |
|
} |
|
|
|
// Delay delay duration. |
|
func (td *TimerData) Delay() itime.Duration { |
|
return td.expire.Sub(itime.Now()) |
|
} |
|
|
|
// ExpireString expire string. |
|
func (td *TimerData) ExpireString() string { |
|
return td.expire.Format(timerFormat) |
|
} |
|
|
|
// Timer timer. |
|
type Timer struct { |
|
lock sync.Mutex |
|
free *TimerData |
|
timers []*TimerData |
|
signal *itime.Timer |
|
num int |
|
} |
|
|
|
// NewTimer new a timer. |
|
// A heap must be initialized before any of the heap operations |
|
// can be used. Init is idempotent with respect to the heap invariants |
|
// and may be called whenever the heap invariants may have been invalidated. |
|
// Its complexity is O(n) where n = h.Len(). |
|
// |
|
func NewTimer(num int) (t *Timer) { |
|
t = new(Timer) |
|
t.init(num) |
|
return t |
|
} |
|
|
|
// Init init the timer. |
|
func (t *Timer) Init(num int) { |
|
t.init(num) |
|
} |
|
|
|
func (t *Timer) init(num int) { |
|
t.signal = itime.NewTimer(infiniteDuration) |
|
t.timers = make([]*TimerData, 0, num) |
|
t.num = num |
|
t.grow() |
|
go t.start() |
|
} |
|
|
|
func (t *Timer) grow() { |
|
var ( |
|
i int |
|
td *TimerData |
|
tds = make([]TimerData, t.num) |
|
) |
|
t.free = &(tds[0]) |
|
td = t.free |
|
for i = 1; i < t.num; i++ { |
|
td.next = &(tds[i]) |
|
td = td.next |
|
} |
|
td.next = nil |
|
} |
|
|
|
// get get a free timer data. |
|
func (t *Timer) get() (td *TimerData) { |
|
if td = t.free; td == nil { |
|
t.grow() |
|
td = t.free |
|
} |
|
t.free = td.next |
|
return |
|
} |
|
|
|
// put put back a timer data. |
|
func (t *Timer) put(td *TimerData) { |
|
td.fn = nil |
|
td.next = t.free |
|
t.free = td |
|
} |
|
|
|
// Add add the element x onto the heap. The complexity is |
|
// O(log(n)) where n = h.Len(). |
|
func (t *Timer) Add(expire itime.Duration, fn func()) (td *TimerData) { |
|
t.lock.Lock() |
|
td = t.get() |
|
td.expire = itime.Now().Add(expire) |
|
td.fn = fn |
|
t.add(td) |
|
t.lock.Unlock() |
|
return |
|
} |
|
|
|
// Del removes the element at index i from the heap. |
|
// The complexity is O(log(n)) where n = h.Len(). |
|
func (t *Timer) Del(td *TimerData) { |
|
t.lock.Lock() |
|
t.del(td) |
|
t.put(td) |
|
t.lock.Unlock() |
|
} |
|
|
|
// Push pushes the element x onto the heap. The complexity is |
|
// O(log(n)) where n = h.Len(). |
|
func (t *Timer) add(td *TimerData) { |
|
var d itime.Duration |
|
td.index = len(t.timers) |
|
// add to the minheap last node |
|
t.timers = append(t.timers, td) |
|
t.up(td.index) |
|
if td.index == 0 { |
|
// if first node, signal start goroutine |
|
d = td.Delay() |
|
t.signal.Reset(d) |
|
if Debug { |
|
log.Info("timer: add reset delay %d ms", int64(d)/int64(itime.Millisecond)) |
|
} |
|
} |
|
if Debug { |
|
log.Info("timer: push item key: %s, expire: %s, index: %d", td.Key, td.ExpireString(), td.index) |
|
} |
|
} |
|
|
|
func (t *Timer) del(td *TimerData) { |
|
var ( |
|
i = td.index |
|
last = len(t.timers) - 1 |
|
) |
|
if i < 0 || i > last || t.timers[i] != td { |
|
// already remove, usually by expire |
|
if Debug { |
|
log.Info("timer del i: %d, last: %d, %p", i, last, td) |
|
} |
|
return |
|
} |
|
if i != last { |
|
t.swap(i, last) |
|
t.down(i, last) |
|
t.up(i) |
|
} |
|
// remove item is the last node |
|
t.timers[last].index = -1 // for safety |
|
t.timers = t.timers[:last] |
|
if Debug { |
|
log.Info("timer: remove item key: %s, expire: %s, index: %d", td.Key, td.ExpireString(), td.index) |
|
} |
|
} |
|
|
|
// Set update timer data. |
|
func (t *Timer) Set(td *TimerData, expire itime.Duration) { |
|
t.lock.Lock() |
|
t.del(td) |
|
td.expire = itime.Now().Add(expire) |
|
t.add(td) |
|
t.lock.Unlock() |
|
} |
|
|
|
// start start the timer. |
|
func (t *Timer) start() { |
|
for { |
|
t.expire() |
|
<-t.signal.C |
|
} |
|
} |
|
|
|
// expire removes the minimum element (according to Less) from the heap. |
|
// The complexity is O(log(n)) where n = max. |
|
// It is equivalent to Del(0). |
|
func (t *Timer) expire() { |
|
var ( |
|
fn func() |
|
td *TimerData |
|
d itime.Duration |
|
) |
|
t.lock.Lock() |
|
for { |
|
if len(t.timers) == 0 { |
|
d = infiniteDuration |
|
if Debug { |
|
log.Info("timer: no other instance") |
|
} |
|
break |
|
} |
|
td = t.timers[0] |
|
if d = td.Delay(); d > 0 { |
|
break |
|
} |
|
fn = td.fn |
|
// let caller put back |
|
t.del(td) |
|
t.lock.Unlock() |
|
if fn == nil { |
|
log.Warn("expire timer no fn") |
|
} else { |
|
if Debug { |
|
log.Info("timer key: %s, expire: %s, index: %d expired, call fn", td.Key, td.ExpireString(), td.index) |
|
} |
|
fn() |
|
} |
|
t.lock.Lock() |
|
} |
|
t.signal.Reset(d) |
|
if Debug { |
|
log.Info("timer: expier reset delay %d ms", int64(d)/int64(itime.Millisecond)) |
|
} |
|
t.lock.Unlock() |
|
} |
|
|
|
func (t *Timer) up(j int) { |
|
for { |
|
i := (j - 1) / 2 // parent |
|
if i <= j || !t.less(j, i) { |
|
break |
|
} |
|
t.swap(i, j) |
|
j = i |
|
} |
|
} |
|
|
|
func (t *Timer) down(i, n int) { |
|
for { |
|
j1 := 2*i + 1 |
|
if j1 >= n || j1 < 0 { // j1 < 0 after int overflow |
|
break |
|
} |
|
j := j1 // left child |
|
if j2 := j1 + 1; j2 < n && !t.less(j1, j2) { |
|
j = j2 // = 2*i + 2 // right child |
|
} |
|
if !t.less(j, i) { |
|
break |
|
} |
|
t.swap(i, j) |
|
i = j |
|
} |
|
} |
|
|
|
func (t *Timer) less(i, j int) bool { |
|
return t.timers[i].expire.Before(t.timers[j].expire) |
|
} |
|
|
|
func (t *Timer) swap(i, j int) { |
|
t.timers[i], t.timers[j] = t.timers[j], t.timers[i] |
|
t.timers[i].index = i |
|
t.timers[j].index = j |
|
}
|
|
|