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  }