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.
254 lines
5.4 KiB
254 lines
5.4 KiB
package gocron
|
|
|
|
import (
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
// Scheduler struct, the only data member is the list of jobs.
|
|
// - implements the sort.Interface{} for sorting jobs, by the time nextRun
|
|
type Scheduler struct {
|
|
jobs [MAXJOBNUM]*Job // Array store jobs
|
|
size int // Size of jobs which jobs holding.
|
|
loc *time.Location // Location to use when scheduling jobs with specified times
|
|
}
|
|
|
|
var (
|
|
defaultScheduler = NewScheduler()
|
|
)
|
|
|
|
// NewScheduler creates a new scheduler
|
|
func NewScheduler() *Scheduler {
|
|
return &Scheduler{
|
|
jobs: [MAXJOBNUM]*Job{},
|
|
size: 0,
|
|
loc: loc,
|
|
}
|
|
}
|
|
|
|
// Jobs returns the list of Jobs from the Scheduler
|
|
func (s *Scheduler) Jobs() []*Job {
|
|
return s.jobs[:s.size]
|
|
}
|
|
|
|
func (s *Scheduler) Len() int {
|
|
return s.size
|
|
}
|
|
|
|
func (s *Scheduler) Swap(i, j int) {
|
|
s.jobs[i], s.jobs[j] = s.jobs[j], s.jobs[i]
|
|
}
|
|
|
|
func (s *Scheduler) Less(i, j int) bool {
|
|
return s.jobs[j].nextRun.Unix() >= s.jobs[i].nextRun.Unix()
|
|
}
|
|
|
|
// ChangeLoc changes the default time location
|
|
func (s *Scheduler) ChangeLoc(newLocation *time.Location) {
|
|
s.loc = newLocation
|
|
}
|
|
|
|
// Get the current runnable jobs, which shouldRun is True
|
|
func (s *Scheduler) getRunnableJobs() (runningJobs [MAXJOBNUM]*Job, n int) {
|
|
runnableJobs := [MAXJOBNUM]*Job{}
|
|
n = 0
|
|
sort.Sort(s)
|
|
for i := 0; i < s.size; i++ {
|
|
if s.jobs[i].shouldRun() {
|
|
runnableJobs[n] = s.jobs[i]
|
|
n++
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
return runnableJobs, n
|
|
}
|
|
|
|
// NextRun datetime when the next job should run.
|
|
func (s *Scheduler) NextRun() (*Job, time.Time) {
|
|
if s.size <= 0 {
|
|
return nil, time.Now()
|
|
}
|
|
sort.Sort(s)
|
|
return s.jobs[0], s.jobs[0].nextRun
|
|
}
|
|
|
|
// Every schedule a new periodic job with interval
|
|
func (s *Scheduler) Every(interval uint64) *Job {
|
|
job := NewJob(interval).Loc(s.loc)
|
|
s.jobs[s.size] = job
|
|
s.size++
|
|
return job
|
|
}
|
|
|
|
// RunPending runs all the jobs that are scheduled to run.
|
|
func (s *Scheduler) RunPending() {
|
|
runnableJobs, n := s.getRunnableJobs()
|
|
|
|
if n != 0 {
|
|
for i := 0; i < n; i++ {
|
|
go runnableJobs[i].run()
|
|
runnableJobs[i].lastRun = time.Now()
|
|
runnableJobs[i].scheduleNextRun()
|
|
}
|
|
}
|
|
}
|
|
|
|
// RunAll run all jobs regardless if they are scheduled to run or not
|
|
func (s *Scheduler) RunAll() {
|
|
s.RunAllwithDelay(0)
|
|
}
|
|
|
|
// RunAllwithDelay runs all jobs with delay seconds
|
|
func (s *Scheduler) RunAllwithDelay(d int) {
|
|
for i := 0; i < s.size; i++ {
|
|
go s.jobs[i].run()
|
|
if 0 != d {
|
|
time.Sleep(time.Duration(d))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove specific job j by function
|
|
func (s *Scheduler) Remove(j interface{}) {
|
|
s.removeByCondition(func(someJob *Job) bool {
|
|
return someJob.jobFunc == getFunctionName(j)
|
|
})
|
|
}
|
|
|
|
// RemoveByRef removes specific job j by reference
|
|
func (s *Scheduler) RemoveByRef(j *Job) {
|
|
s.removeByCondition(func(someJob *Job) bool {
|
|
return someJob == j
|
|
})
|
|
}
|
|
|
|
func (s *Scheduler) removeByCondition(shouldRemove func(*Job) bool) {
|
|
i := 0
|
|
|
|
// keep deleting until no more jobs match the criteria
|
|
for {
|
|
found := false
|
|
|
|
for ; i < s.size; i++ {
|
|
if shouldRemove(s.jobs[i]) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return
|
|
}
|
|
|
|
for j := (i + 1); j < s.size; j++ {
|
|
s.jobs[i] = s.jobs[j]
|
|
i++
|
|
}
|
|
s.size--
|
|
s.jobs[s.size] = nil
|
|
}
|
|
}
|
|
|
|
// Scheduled checks if specific job j was already added
|
|
func (s *Scheduler) Scheduled(j interface{}) bool {
|
|
for _, job := range s.jobs {
|
|
if job.jobFunc == getFunctionName(j) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Clear delete all scheduled jobs
|
|
func (s *Scheduler) Clear() {
|
|
for i := 0; i < s.size; i++ {
|
|
s.jobs[i] = nil
|
|
}
|
|
s.size = 0
|
|
}
|
|
|
|
// Start all the pending jobs
|
|
// Add seconds ticker
|
|
func (s *Scheduler) Start() chan bool {
|
|
stopped := make(chan bool, 1)
|
|
ticker := time.NewTicker(1 * time.Second)
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
s.RunPending()
|
|
case <-stopped:
|
|
ticker.Stop()
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
return stopped
|
|
}
|
|
|
|
// The following methods are shortcuts for not having to
|
|
// create a Scheduler instance
|
|
|
|
// Every schedules a new periodic job running in specific interval
|
|
func Every(interval uint64) *Job {
|
|
return defaultScheduler.Every(interval)
|
|
}
|
|
|
|
// RunPending run all jobs that are scheduled to run
|
|
//
|
|
// Please note that it is *intended behavior that run_pending()
|
|
// does not run missed jobs*. For example, if you've registered a job
|
|
// that should run every minute and you only call run_pending()
|
|
// in one hour increments then your job won't be run 60 times in
|
|
// between but only once.
|
|
func RunPending() {
|
|
defaultScheduler.RunPending()
|
|
}
|
|
|
|
// RunAll run all jobs regardless if they are scheduled to run or not.
|
|
func RunAll() {
|
|
defaultScheduler.RunAll()
|
|
}
|
|
|
|
// RunAllwithDelay run all the jobs with a delay in seconds
|
|
//
|
|
// A delay of `delay` seconds is added between each job. This can help
|
|
// to distribute the system load generated by the jobs more evenly over
|
|
// time.
|
|
func RunAllwithDelay(d int) {
|
|
defaultScheduler.RunAllwithDelay(d)
|
|
}
|
|
|
|
// Start run all jobs that are scheduled to run
|
|
func Start() chan bool {
|
|
return defaultScheduler.Start()
|
|
}
|
|
|
|
// Clear all scheduled jobs
|
|
func Clear() {
|
|
defaultScheduler.Clear()
|
|
}
|
|
|
|
// Remove a specific job
|
|
func Remove(j interface{}) {
|
|
defaultScheduler.Remove(j)
|
|
}
|
|
|
|
// Scheduled checks if specific job j was already added
|
|
func Scheduled(j interface{}) bool {
|
|
for _, job := range defaultScheduler.jobs {
|
|
if job.jobFunc == getFunctionName(j) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// NextRun gets the next running time
|
|
func NextRun() (job *Job, time time.Time) {
|
|
return defaultScheduler.NextRun()
|
|
}
|