first commit
This commit is contained in:
6
pkg/time/debug.go
Normal file
6
pkg/time/debug.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package time
|
||||
|
||||
const (
|
||||
// Debug debug switch
|
||||
Debug = false
|
||||
)
|
||||
17
pkg/time/duration.go
Normal file
17
pkg/time/duration.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
xtime "time"
|
||||
)
|
||||
|
||||
// Duration be used toml unmarshal string time, like 1s, 500ms.
|
||||
type Duration xtime.Duration
|
||||
|
||||
// UnmarshalText unmarshal text to duration.
|
||||
func (d *Duration) UnmarshalText(text []byte) error {
|
||||
tmp, err := xtime.ParseDuration(string(text))
|
||||
if err == nil {
|
||||
*d = Duration(tmp)
|
||||
}
|
||||
return err
|
||||
}
|
||||
20
pkg/time/duration_test.go
Normal file
20
pkg/time/duration_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDurationText(t *testing.T) {
|
||||
var (
|
||||
input = []byte("10s")
|
||||
output = time.Second * 10
|
||||
d Duration
|
||||
)
|
||||
if err := d.UnmarshalText(input); err != nil {
|
||||
t.FailNow()
|
||||
}
|
||||
if int64(output) != int64(d) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
263
pkg/time/timer.go
Normal file
263
pkg/time/timer.go
Normal file
@@ -0,0 +1,263 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
itime "time"
|
||||
)
|
||||
|
||||
const (
|
||||
timerFormat = "2006-01-02 15:04:05"
|
||||
infiniteDuration = itime.Duration(1<<63 - 1)
|
||||
)
|
||||
|
||||
// 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 itime.Until(td.expire)
|
||||
}
|
||||
|
||||
// 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.Printf("timer: add reset delay %d ms", int64(d)/int64(itime.Millisecond))
|
||||
}
|
||||
}
|
||||
if Debug {
|
||||
log.Printf("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.Printf("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.Printf("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.Print("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.Print("expire timer no fn")
|
||||
} else {
|
||||
if Debug {
|
||||
log.Printf("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.Printf("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
|
||||
}
|
||||
43
pkg/time/timer_test.go
Normal file
43
pkg/time/timer_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
)
|
||||
|
||||
func TestTimer(t *testing.T) {
|
||||
timer := NewTimer(100)
|
||||
tds := make([]*TimerData, 100)
|
||||
for i := 0; i < 100; i++ {
|
||||
tds[i] = timer.Add(time.Duration(i)*time.Second+5*time.Minute, nil)
|
||||
}
|
||||
printTimer(timer)
|
||||
for i := 0; i < 100; i++ {
|
||||
log.Infof("td: %s, %s, %d", tds[i].Key, tds[i].ExpireString(), tds[i].index)
|
||||
timer.Del(tds[i])
|
||||
}
|
||||
printTimer(timer)
|
||||
for i := 0; i < 100; i++ {
|
||||
tds[i] = timer.Add(time.Duration(i)*time.Second+5*time.Minute, nil)
|
||||
}
|
||||
printTimer(timer)
|
||||
for i := 0; i < 100; i++ {
|
||||
timer.Del(tds[i])
|
||||
}
|
||||
printTimer(timer)
|
||||
timer.Add(time.Second, nil)
|
||||
time.Sleep(time.Second * 2)
|
||||
if len(timer.timers) != 0 {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func printTimer(timer *Timer) {
|
||||
log.Infof("----------timers: %d ----------", len(timer.timers))
|
||||
for i := 0; i < len(timer.timers); i++ {
|
||||
log.Infof("timer: %s, %s, index: %d", timer.timers[i].Key, timer.timers[i].ExpireString(), timer.timers[i].index)
|
||||
}
|
||||
log.Infof("--------------------")
|
||||
}
|
||||
Reference in New Issue
Block a user