2021-01-17 21:46:25 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
2018-10-25 10:08:08 +08:00
|
|
|
//
|
|
|
|
// 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,
|
2019-02-02 16:18:25 +08:00
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-10-25 10:08:08 +08:00
|
|
|
|
|
|
|
package gcron
|
|
|
|
|
2018-12-30 11:08:07 +08:00
|
|
|
import (
|
2019-05-05 22:57:13 +08:00
|
|
|
"reflect"
|
|
|
|
"runtime"
|
|
|
|
"time"
|
2019-07-29 21:01:19 +08:00
|
|
|
|
|
|
|
"github.com/gogf/gf/container/gtype"
|
|
|
|
"github.com/gogf/gf/os/glog"
|
|
|
|
"github.com/gogf/gf/os/gtimer"
|
|
|
|
"github.com/gogf/gf/util/gconv"
|
2018-12-30 11:08:07 +08:00
|
|
|
)
|
|
|
|
|
2019-04-17 23:50:37 +08:00
|
|
|
// Timed task entry.
|
2021-05-15 18:13:51 +08:00
|
|
|
type Job struct {
|
2019-06-19 09:06:52 +08:00
|
|
|
cron *Cron // Cron object belonged to.
|
2021-05-15 18:13:51 +08:00
|
|
|
job *gtimer.Job // Associated gtimer.Job.
|
2019-06-19 09:06:52 +08:00
|
|
|
schedule *cronSchedule // Timed schedule object.
|
|
|
|
jobName string // Callback function name(address info).
|
|
|
|
times *gtype.Int // Running times limit.
|
2021-05-15 18:13:51 +08:00
|
|
|
Name string // Job name.
|
2019-06-19 09:06:52 +08:00
|
|
|
Job func() `json:"-"` // Callback function.
|
|
|
|
Time time.Time // Registered time.
|
2018-12-30 11:08:07 +08:00
|
|
|
}
|
|
|
|
|
2021-05-15 18:13:51 +08:00
|
|
|
// addJob creates and returns a new Job object.
|
2019-04-17 23:50:37 +08:00
|
|
|
// Param <job> is the callback function for timed task execution.
|
|
|
|
// Param <singleton> specifies whether timed task executing in singleton mode.
|
|
|
|
// Param <name> names this entry for manual control.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (c *Cron) addJob(pattern string, job func(), singleton bool, name ...string) (*Job, error) {
|
2019-06-19 09:06:52 +08:00
|
|
|
schedule, err := newSchedule(pattern)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// No limit for <times>, for gtimer checking scheduling every second.
|
2021-05-15 18:13:51 +08:00
|
|
|
entry := &Job{
|
2019-06-19 09:06:52 +08:00
|
|
|
cron: c,
|
|
|
|
schedule: schedule,
|
|
|
|
jobName: runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(),
|
2021-01-22 23:09:42 +08:00
|
|
|
times: gtype.NewInt(defaultTimes),
|
2019-06-19 09:06:52 +08:00
|
|
|
Job: job,
|
|
|
|
Time: time.Now(),
|
|
|
|
}
|
|
|
|
if len(name) > 0 {
|
|
|
|
entry.Name = name[0]
|
|
|
|
} else {
|
|
|
|
entry.Name = "gcron-" + gconv.String(c.idGen.Add(1))
|
|
|
|
}
|
2019-05-05 22:57:13 +08:00
|
|
|
// When you add a scheduled task, you cannot allow it to run.
|
|
|
|
// It cannot start running when added to gtimer.
|
|
|
|
// It should start running after the entry is added to the entries map,
|
|
|
|
// to avoid the task from running during adding where the entries
|
|
|
|
// does not have the entry information, which might cause panic.
|
2021-05-15 18:13:51 +08:00
|
|
|
entry.job = gtimer.AddJob(time.Second, entry.check, singleton, -1, gtimer.StatusStopped)
|
2019-06-19 09:06:52 +08:00
|
|
|
c.entries.Set(entry.Name, entry)
|
2021-05-15 18:13:51 +08:00
|
|
|
entry.job.Start()
|
2019-06-19 09:06:52 +08:00
|
|
|
return entry, nil
|
2018-12-30 11:08:07 +08:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// IsSingleton return whether this entry is a singleton timed task.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (entry *Job) IsSingleton() bool {
|
|
|
|
return entry.job.IsSingleton()
|
2019-01-16 22:34:22 +08:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// SetSingleton sets the entry running in singleton mode.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (entry *Job) SetSingleton(enabled bool) {
|
|
|
|
entry.job.SetSingleton(true)
|
2019-01-16 22:34:22 +08:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// SetTimes sets the times which the entry can run.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (entry *Job) SetTimes(times int) {
|
2019-06-19 09:06:52 +08:00
|
|
|
entry.times.Set(times)
|
2018-12-30 11:08:07 +08:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// Status returns the status of entry.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (entry *Job) Status() int {
|
|
|
|
return entry.job.Status()
|
2018-12-30 11:08:07 +08:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// SetStatus sets the status of the entry.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (entry *Job) SetStatus(status int) int {
|
|
|
|
return entry.job.SetStatus(status)
|
2019-01-16 22:34:22 +08:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// Start starts running the entry.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (entry *Job) Start() {
|
|
|
|
entry.job.Start()
|
2018-10-25 10:08:08 +08:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// Stop stops running the entry.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (entry *Job) Stop() {
|
|
|
|
entry.job.Stop()
|
2018-10-25 10:08:08 +08:00
|
|
|
}
|
2019-01-16 22:34:22 +08:00
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// Close stops and removes the entry from cron.
|
2021-05-15 18:13:51 +08:00
|
|
|
func (entry *Job) Close() {
|
2019-06-19 09:06:52 +08:00
|
|
|
entry.cron.entries.Remove(entry.Name)
|
2021-05-15 18:13:51 +08:00
|
|
|
entry.job.Close()
|
2019-01-21 22:09:51 +08:00
|
|
|
}
|
|
|
|
|
2019-05-05 22:57:13 +08:00
|
|
|
// Timed task check execution.
|
2021-05-15 18:13:51 +08:00
|
|
|
// The running times limits feature is implemented by gcron.Job and cannot be implemented by gtimer.Job.
|
|
|
|
// gcron.Job relies on gtimer to implement a scheduled task check for gcron.Job per second.
|
|
|
|
func (entry *Job) check() {
|
2019-06-19 09:06:52 +08:00
|
|
|
if entry.schedule.meet(time.Now()) {
|
|
|
|
path := entry.cron.GetLogPath()
|
|
|
|
level := entry.cron.GetLogLevel()
|
|
|
|
switch entry.cron.status.Val() {
|
2020-12-14 13:26:48 +08:00
|
|
|
case StatusStopped:
|
2019-06-19 09:06:52 +08:00
|
|
|
return
|
|
|
|
|
2020-12-14 13:26:48 +08:00
|
|
|
case StatusClosed:
|
2019-06-19 09:06:52 +08:00
|
|
|
glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s removed", entry.Name, entry.schedule.pattern, entry.jobName)
|
|
|
|
entry.Close()
|
|
|
|
|
2020-12-14 13:26:48 +08:00
|
|
|
case StatusReady:
|
2019-06-19 09:06:52 +08:00
|
|
|
fallthrough
|
2020-12-14 13:26:48 +08:00
|
|
|
case StatusRunning:
|
2019-06-19 09:06:52 +08:00
|
|
|
// Running times check.
|
|
|
|
times := entry.times.Add(-1)
|
|
|
|
if times <= 0 {
|
2021-05-15 18:13:51 +08:00
|
|
|
if entry.job.SetStatus(StatusClosed) == StatusClosed || times < 0 {
|
2019-06-19 09:06:52 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if times < 2000000000 && times > 1000000000 {
|
2021-01-22 23:09:42 +08:00
|
|
|
entry.times.Set(defaultTimes)
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
|
|
|
glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName)
|
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
glog.Path(path).Level(level).Errorf("[gcron] %s(%s) %s end with error: %v", entry.Name, entry.schedule.pattern, entry.jobName, err)
|
|
|
|
} else {
|
|
|
|
glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName)
|
|
|
|
}
|
2021-05-15 18:13:51 +08:00
|
|
|
if entry.job.Status() == StatusClosed {
|
2019-06-19 09:06:52 +08:00
|
|
|
entry.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
entry.Job()
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2019-01-16 22:34:22 +08:00
|
|
|
}
|