github.com/gogf/gf@v1.16.9/os/gcron/gcron_entry.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package gcron 8 9 import ( 10 "github.com/gogf/gf/errors/gcode" 11 "github.com/gogf/gf/errors/gerror" 12 "reflect" 13 "runtime" 14 "time" 15 16 "github.com/gogf/gf/container/gtype" 17 "github.com/gogf/gf/os/gtimer" 18 "github.com/gogf/gf/util/gconv" 19 ) 20 21 // Entry is timing task entry. 22 type Entry struct { 23 cron *Cron // Cron object belonged to. 24 timerEntry *gtimer.Entry // Associated timer Entry. 25 schedule *cronSchedule // Timed schedule object. 26 jobName string // Callback function name(address info). 27 times *gtype.Int // Running times limit. 28 infinite *gtype.Bool // No times limit. 29 Name string // Entry name. 30 Job func() `json:"-"` // Callback function. 31 Time time.Time // Registered time. 32 } 33 34 type addEntryInput struct { 35 Name string // Name names this entry for manual control. 36 Job func() // Job is the callback function for timed task execution. 37 Times int // Times specifies the running limit times for the entry. 38 Pattern string // Pattern is the crontab style string for scheduler. 39 Singleton bool // Singleton specifies whether timed task executing in singleton mode. 40 Infinite bool // Infinite specifies whether this entry is running with no times limit. 41 } 42 43 // doAddEntry creates and returns a new Entry object. 44 func (c *Cron) doAddEntry(in addEntryInput) (*Entry, error) { 45 if in.Name != "" { 46 if c.Search(in.Name) != nil { 47 return nil, gerror.NewCodef(gcode.CodeInvalidOperation, `cron job "%s" already exists`, in.Name) 48 } 49 } 50 51 schedule, err := newSchedule(in.Pattern) 52 if err != nil { 53 return nil, err 54 } 55 // No limit for `times`, for timer checking scheduling every second. 56 entry := &Entry{ 57 cron: c, 58 schedule: schedule, 59 jobName: runtime.FuncForPC(reflect.ValueOf(in.Job).Pointer()).Name(), 60 times: gtype.NewInt(in.Times), 61 infinite: gtype.NewBool(in.Infinite), 62 Job: in.Job, 63 Time: time.Now(), 64 } 65 if in.Name != "" { 66 entry.Name = in.Name 67 } else { 68 entry.Name = "cron-" + gconv.String(c.idGen.Add(1)) 69 } 70 // When you add a scheduled task, you cannot allow it to run. 71 // It cannot start running when added to timer. 72 // It should start running after the entry is added to the Cron entries map, to avoid the task 73 // from running during adding where the entries do not have the entry information, which might cause panic. 74 entry.timerEntry = gtimer.AddEntry(time.Second, entry.check, in.Singleton, -1, gtimer.StatusStopped) 75 c.entries.Set(entry.Name, entry) 76 entry.timerEntry.Start() 77 return entry, nil 78 } 79 80 // IsSingleton return whether this entry is a singleton timed task. 81 func (entry *Entry) IsSingleton() bool { 82 return entry.timerEntry.IsSingleton() 83 } 84 85 // SetSingleton sets the entry running in singleton mode. 86 func (entry *Entry) SetSingleton(enabled bool) { 87 entry.timerEntry.SetSingleton(enabled) 88 } 89 90 // SetTimes sets the times which the entry can run. 91 func (entry *Entry) SetTimes(times int) { 92 entry.times.Set(times) 93 entry.infinite.Set(false) 94 } 95 96 // Status returns the status of entry. 97 func (entry *Entry) Status() int { 98 return entry.timerEntry.Status() 99 } 100 101 // SetStatus sets the status of the entry. 102 func (entry *Entry) SetStatus(status int) int { 103 return entry.timerEntry.SetStatus(status) 104 } 105 106 // Start starts running the entry. 107 func (entry *Entry) Start() { 108 entry.timerEntry.Start() 109 } 110 111 // Stop stops running the entry. 112 func (entry *Entry) Stop() { 113 entry.timerEntry.Stop() 114 } 115 116 // Close stops and removes the entry from cron. 117 func (entry *Entry) Close() { 118 entry.cron.entries.Remove(entry.Name) 119 entry.timerEntry.Close() 120 } 121 122 // check is the core timing task check logic. 123 // The running times limits feature is implemented by gcron.Entry and cannot be implemented by gtimer.Entry. 124 // gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second. 125 func (entry *Entry) check() { 126 if entry.schedule.meet(time.Now()) { 127 switch entry.cron.status.Val() { 128 case StatusStopped: 129 return 130 131 case StatusClosed: 132 entry.logDebugf("[gcron] %s %s removed", entry.schedule.pattern, entry.jobName) 133 entry.Close() 134 135 case StatusReady: 136 fallthrough 137 case StatusRunning: 138 defer func() { 139 if err := recover(); err != nil { 140 entry.logErrorf("[gcron] %s %s end with error: %+v", entry.schedule.pattern, entry.jobName, err) 141 } else { 142 entry.logDebugf("[gcron] %s %s end", entry.schedule.pattern, entry.jobName) 143 } 144 145 if entry.timerEntry.Status() == StatusClosed { 146 entry.Close() 147 } 148 }() 149 150 // Running times check. 151 if !entry.infinite.Val() { 152 times := entry.times.Add(-1) 153 if times <= 0 { 154 if entry.timerEntry.SetStatus(StatusClosed) == StatusClosed || times < 0 { 155 return 156 } 157 } 158 } 159 entry.logDebugf("[gcron] %s %s start", entry.schedule.pattern, entry.jobName) 160 161 entry.Job() 162 } 163 } 164 } 165 func (entry *Entry) logDebugf(format string, v ...interface{}) { 166 if logger := entry.cron.GetLogger(); logger != nil { 167 logger.Debugf(format, v...) 168 } 169 } 170 171 func (entry *Entry) logErrorf(format string, v ...interface{}) { 172 if logger := entry.cron.GetLogger(); logger != nil { 173 logger.Errorf(format, v...) 174 } 175 }