2017-04-14 10:09:51 +00:00
|
|
|
package cron
|
|
|
|
|
|
|
|
import (
|
2017-04-15 11:32:56 +00:00
|
|
|
"errors"
|
2017-04-14 10:09:51 +00:00
|
|
|
"reflect"
|
|
|
|
"runtime"
|
|
|
|
"sort"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const MAX_TASKS = 10000
|
|
|
|
|
|
|
|
type Task struct {
|
|
|
|
id string
|
|
|
|
intervalSec uint64
|
|
|
|
TaskFunc string
|
|
|
|
period time.Duration
|
|
|
|
taskFunc map[string]interface{}
|
|
|
|
funcParams map[string]([]interface{})
|
|
|
|
lastAt time.Time
|
|
|
|
nextAt time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewTask(id string, intervel uint64) *Task {
|
|
|
|
return &Task{
|
|
|
|
id,
|
|
|
|
intervel,
|
|
|
|
"",
|
|
|
|
0,
|
|
|
|
make(map[string]interface{}),
|
|
|
|
make(map[string]([]interface{})),
|
|
|
|
time.Unix(0, 0),
|
|
|
|
time.Unix(0, 0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Task) runnable() bool {
|
|
|
|
return time.Now().After(t.nextAt)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Task) run() {
|
|
|
|
taskFunc := reflect.ValueOf(t.taskFunc[t.TaskFunc])
|
|
|
|
taskParams := t.funcParams[t.TaskFunc]
|
|
|
|
in := make([]reflect.Value, len(taskParams))
|
|
|
|
for i, param := range taskParams {
|
|
|
|
in[i] = reflect.ValueOf(param)
|
|
|
|
}
|
|
|
|
taskFunc.Call(in)
|
|
|
|
t.lastAt = time.Now()
|
|
|
|
t.addNextAt()
|
|
|
|
}
|
|
|
|
|
2017-04-15 11:32:56 +00:00
|
|
|
func (t *Task) Invoke(TaskFunc interface{}, params ...interface{}) error {
|
2017-04-14 10:09:51 +00:00
|
|
|
if reflect.TypeOf(TaskFunc).Kind() != reflect.Func {
|
2017-04-15 11:32:56 +00:00
|
|
|
return errors.New("Not a function type.")
|
2017-04-14 10:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
funcName := runtime.FuncForPC(reflect.ValueOf((TaskFunc)).Pointer()).Name()
|
|
|
|
t.taskFunc[funcName] = TaskFunc
|
|
|
|
t.funcParams[funcName] = params
|
|
|
|
t.TaskFunc = funcName
|
|
|
|
t.addNextAt()
|
2017-04-15 11:32:56 +00:00
|
|
|
|
|
|
|
return nil
|
2017-04-14 10:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Task) addNextAt() {
|
|
|
|
if t.lastAt == time.Unix(0, 0) {
|
|
|
|
t.lastAt = time.Now()
|
|
|
|
}
|
|
|
|
|
|
|
|
if t.period == 0 {
|
|
|
|
t.period = time.Duration(t.intervalSec)
|
2017-04-17 08:20:21 +00:00
|
|
|
t.nextAt = t.lastAt.Add(1 * time.Second)
|
2017-04-15 11:32:56 +00:00
|
|
|
} else {
|
2017-04-17 08:20:21 +00:00
|
|
|
t.nextAt = t.lastAt.Add((t.period * time.Second) - time.Second)
|
2017-04-14 10:09:51 +00:00
|
|
|
}
|
2017-04-15 11:32:56 +00:00
|
|
|
|
2017-04-14 10:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Cron struct {
|
|
|
|
Tasks [MAX_TASKS]*Task
|
|
|
|
size int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) Len() int {
|
|
|
|
return c.size
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) Swap(i, j int) {
|
|
|
|
c.Tasks[i], c.Tasks[j] = c.Tasks[j], c.Tasks[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) Less(i, j int) bool {
|
|
|
|
return c.Tasks[j].nextAt.After(c.Tasks[i].nextAt)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewCron() *Cron {
|
|
|
|
return &Cron{[MAX_TASKS]*Task{}, 0}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) RunnableTasks() ([MAX_TASKS]*Task, int) {
|
|
|
|
runnableTasks := [MAX_TASKS]*Task{}
|
|
|
|
n := 0
|
|
|
|
sort.Sort(c)
|
|
|
|
for i := 0; i < c.size; i++ {
|
|
|
|
if c.Tasks[i].runnable() {
|
|
|
|
runnableTasks[n] = c.Tasks[i]
|
|
|
|
n++
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return runnableTasks, n
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) nextRun() (*Task, time.Time) {
|
|
|
|
if c.size <= 0 {
|
|
|
|
return nil, time.Now()
|
|
|
|
}
|
|
|
|
sort.Sort(c)
|
|
|
|
return c.Tasks[0], c.Tasks[0].nextAt
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) addTask(id string, intervalSec uint64) *Task {
|
|
|
|
Task := NewTask(id, intervalSec)
|
|
|
|
c.Tasks[c.size] = Task
|
|
|
|
c.size++
|
|
|
|
return Task
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) updateTask(id string, intervalSec uint64) {
|
|
|
|
for i := 0; i < c.Len(); i++ {
|
|
|
|
if c.Tasks[i].id == id {
|
|
|
|
c.Tasks[i].intervalSec = intervalSec
|
|
|
|
c.Tasks[i].period = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) runAll() {
|
|
|
|
runnableTasks, n := c.RunnableTasks()
|
|
|
|
|
|
|
|
if n != 0 {
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
go runnableTasks[i].run()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-15 11:32:56 +00:00
|
|
|
func (c *Cron) remove(id string) error {
|
2017-04-14 10:09:51 +00:00
|
|
|
i := 0
|
|
|
|
|
|
|
|
for ; i < c.size; i++ {
|
|
|
|
if c.Tasks[i].id == id {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for j := (i + 1); j < c.size; j++ {
|
|
|
|
c.Tasks[i] = c.Tasks[j]
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
c.size = c.size - 1
|
|
|
|
|
2017-04-15 11:32:56 +00:00
|
|
|
return nil
|
2017-04-14 10:09:51 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 11:32:56 +00:00
|
|
|
func (c *Cron) removeAll() error {
|
2017-04-14 10:09:51 +00:00
|
|
|
for i := 0; i < c.size; i++ {
|
|
|
|
c.Tasks[i] = nil
|
|
|
|
}
|
|
|
|
c.size = 0
|
2017-04-15 11:32:56 +00:00
|
|
|
|
|
|
|
return nil
|
2017-04-14 10:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Cron) start() chan bool {
|
|
|
|
stopped := make(chan bool, 1)
|
2017-04-17 08:20:21 +00:00
|
|
|
ticker := time.NewTicker(time.Second * 1)
|
2017-04-14 10:09:51 +00:00
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
c.runAll()
|
|
|
|
case <-stopped:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return stopped
|
|
|
|
}
|
|
|
|
|
|
|
|
var cron = NewCron()
|
|
|
|
|
|
|
|
func Start() chan bool {
|
|
|
|
return cron.start()
|
|
|
|
}
|
|
|
|
|
|
|
|
func AddTask(id string, intervalSec uint64) *Task {
|
|
|
|
return cron.addTask(id, intervalSec)
|
|
|
|
}
|
|
|
|
|
|
|
|
func UpdateTask(id string, intervalSec uint64) {
|
|
|
|
cron.updateTask(id, intervalSec)
|
|
|
|
}
|
|
|
|
|
2017-04-15 11:32:56 +00:00
|
|
|
func RemoveAll() error {
|
|
|
|
return cron.removeAll()
|
2017-04-14 10:09:51 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 11:32:56 +00:00
|
|
|
func Remove(id string) error {
|
|
|
|
return cron.remove(id)
|
2017-04-14 10:09:51 +00:00
|
|
|
}
|