github.com/zhongdalu/gf@v1.0.0/g/os/gcron/gcron_entry.go (about)

     1  // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). 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/zhongdalu/gf.
     6  
     7  package gcron
     8  
     9  import (
    10  	"github.com/zhongdalu/gf/g/container/gtype"
    11  	"github.com/zhongdalu/gf/g/os/glog"
    12  	"github.com/zhongdalu/gf/g/os/gtimer"
    13  	"github.com/zhongdalu/gf/g/util/gconv"
    14  	"reflect"
    15  	"runtime"
    16  	"time"
    17  )
    18  
    19  // Timed task entry.
    20  type Entry struct {
    21  	cron     *Cron         // Cron object belonged to.
    22  	entry    *gtimer.Entry // Associated gtimer.Entry.
    23  	schedule *cronSchedule // Timed schedule object.
    24  	jobName  string        // Callback function name(address info).
    25  	times    *gtype.Int    // Running times limit.
    26  	Name     string        // Entry name.
    27  	Job      func()        `json:"-"` // Callback function.
    28  	Time     time.Time     // Registered time.
    29  }
    30  
    31  // addEntry creates and returns a new Entry object.
    32  // Param <job> is the callback function for timed task execution.
    33  // Param <singleton> specifies whether timed task executing in singleton mode.
    34  // Param <name> names this entry for manual control.
    35  func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...string) (*Entry, error) {
    36  	schedule, err := newSchedule(pattern)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	// No limit for <times>, for gtimer checking scheduling every second.
    41  	entry := &Entry{
    42  		cron:     c,
    43  		schedule: schedule,
    44  		jobName:  runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(),
    45  		times:    gtype.NewInt(gDEFAULT_TIMES),
    46  		Job:      job,
    47  		Time:     time.Now(),
    48  	}
    49  	if len(name) > 0 {
    50  		entry.Name = name[0]
    51  	} else {
    52  		entry.Name = "gcron-" + gconv.String(c.idGen.Add(1))
    53  	}
    54  	// When you add a scheduled task, you cannot allow it to run.
    55  	// It cannot start running when added to gtimer.
    56  	// It should start running after the entry is added to the entries map,
    57  	// to avoid the task from running during adding where the entries
    58  	// does not have the entry information, which might cause panic.
    59  	entry.entry = gtimer.AddEntry(time.Second, entry.check, singleton, -1, gtimer.STATUS_STOPPED)
    60  	c.entries.Set(entry.Name, entry)
    61  	entry.entry.Start()
    62  	return entry, nil
    63  }
    64  
    65  // IsSingleton return whether this entry is a singleton timed task.
    66  func (entry *Entry) IsSingleton() bool {
    67  	return entry.entry.IsSingleton()
    68  }
    69  
    70  // SetSingleton sets the entry running in singleton mode.
    71  func (entry *Entry) SetSingleton(enabled bool) {
    72  	entry.entry.SetSingleton(true)
    73  }
    74  
    75  // SetTimes sets the times which the entry can run.
    76  func (entry *Entry) SetTimes(times int) {
    77  	entry.times.Set(times)
    78  }
    79  
    80  // Status returns the status of entry.
    81  func (entry *Entry) Status() int {
    82  	return entry.entry.Status()
    83  }
    84  
    85  // SetStatus sets the status of the entry.
    86  func (entry *Entry) SetStatus(status int) int {
    87  	return entry.entry.SetStatus(status)
    88  }
    89  
    90  // Start starts running the entry.
    91  func (entry *Entry) Start() {
    92  	entry.entry.Start()
    93  }
    94  
    95  // Stop stops running the entry.
    96  func (entry *Entry) Stop() {
    97  	entry.entry.Stop()
    98  }
    99  
   100  // Close stops and removes the entry from cron.
   101  func (entry *Entry) Close() {
   102  	entry.cron.entries.Remove(entry.Name)
   103  	entry.entry.Close()
   104  }
   105  
   106  // Timed task check execution.
   107  // The running times limits feature is implemented by gcron.Entry and cannot be implemented by gtimer.Entry.
   108  // gcron.Entry relies on gtimer to implement a scheduled task check for gcron.Entry per second.
   109  func (entry *Entry) check() {
   110  	if entry.schedule.meet(time.Now()) {
   111  		path := entry.cron.GetLogPath()
   112  		level := entry.cron.GetLogLevel()
   113  		switch entry.cron.status.Val() {
   114  		case STATUS_STOPPED:
   115  			return
   116  
   117  		case STATUS_CLOSED:
   118  			glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s removed", entry.Name, entry.schedule.pattern, entry.jobName)
   119  			entry.Close()
   120  
   121  		case STATUS_READY:
   122  			fallthrough
   123  		case STATUS_RUNNING:
   124  			// Running times check.
   125  			times := entry.times.Add(-1)
   126  			if times <= 0 {
   127  				if entry.entry.SetStatus(STATUS_CLOSED) == STATUS_CLOSED || times < 0 {
   128  					return
   129  				}
   130  			}
   131  			if times < 2000000000 && times > 1000000000 {
   132  				entry.times.Set(gDEFAULT_TIMES)
   133  			}
   134  			glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName)
   135  			defer func() {
   136  				if err := recover(); err != nil {
   137  					glog.Path(path).Level(level).Errorf("[gcron] %s(%s) %s end with error: %v", entry.Name, entry.schedule.pattern, entry.jobName, err)
   138  				} else {
   139  					glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName)
   140  				}
   141  				if entry.entry.Status() == STATUS_CLOSED {
   142  					entry.Close()
   143  				}
   144  			}()
   145  			entry.Job()
   146  
   147  		}
   148  	}
   149  }