github.com/gogf/gf/v2@v2.7.4/os/gtimer/gtimer_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 gtimer
     8  
     9  import (
    10  	"context"
    11  
    12  	"github.com/gogf/gf/v2/errors/gcode"
    13  
    14  	"github.com/gogf/gf/v2/container/gtype"
    15  	"github.com/gogf/gf/v2/errors/gerror"
    16  )
    17  
    18  // Entry is the timing job.
    19  type Entry struct {
    20  	job         JobFunc         // The job function.
    21  	ctx         context.Context // The context for the job, for READ ONLY.
    22  	timer       *Timer          // Belonged timer.
    23  	ticks       int64           // The job runs every tick.
    24  	times       *gtype.Int      // Limit running times.
    25  	status      *gtype.Int      // Job status.
    26  	isSingleton *gtype.Bool     // Singleton mode.
    27  	nextTicks   *gtype.Int64    // Next run ticks of the job.
    28  	infinite    *gtype.Bool     // No times limit.
    29  }
    30  
    31  // JobFunc is the timing called job function in timer.
    32  type JobFunc = func(ctx context.Context)
    33  
    34  // Status returns the status of the job.
    35  func (entry *Entry) Status() int {
    36  	return entry.status.Val()
    37  }
    38  
    39  // Run runs the timer job asynchronously.
    40  func (entry *Entry) Run() {
    41  	if !entry.infinite.Val() {
    42  		leftRunningTimes := entry.times.Add(-1)
    43  		// It checks its running times exceeding.
    44  		if leftRunningTimes < 0 {
    45  			entry.status.Set(StatusClosed)
    46  			return
    47  		}
    48  	}
    49  	go entry.callJobFunc()
    50  }
    51  
    52  // callJobFunc executes the job function in entry.
    53  func (entry *Entry) callJobFunc() {
    54  	defer func() {
    55  		if exception := recover(); exception != nil {
    56  			if exception != panicExit {
    57  				if v, ok := exception.(error); ok && gerror.HasStack(v) {
    58  					panic(v)
    59  				} else {
    60  					panic(gerror.NewCodef(gcode.CodeInternalPanic, "exception recovered: %+v", exception))
    61  				}
    62  			} else {
    63  				entry.Close()
    64  				return
    65  			}
    66  		}
    67  		if entry.Status() == StatusRunning {
    68  			entry.SetStatus(StatusReady)
    69  		}
    70  	}()
    71  	entry.job(entry.ctx)
    72  }
    73  
    74  // doCheckAndRunByTicks checks the if job can run in given timer ticks,
    75  // it runs asynchronously if the given `currentTimerTicks` meets or else
    76  // it increments its ticks and waits for next running check.
    77  func (entry *Entry) doCheckAndRunByTicks(currentTimerTicks int64) {
    78  	// Ticks check.
    79  	if currentTimerTicks < entry.nextTicks.Val() {
    80  		return
    81  	}
    82  	entry.nextTicks.Set(currentTimerTicks + entry.ticks)
    83  	// Perform job checking.
    84  	switch entry.status.Val() {
    85  	case StatusRunning:
    86  		if entry.IsSingleton() {
    87  			return
    88  		}
    89  	case StatusReady:
    90  		if !entry.status.Cas(StatusReady, StatusRunning) {
    91  			return
    92  		}
    93  	case StatusStopped:
    94  		return
    95  	case StatusClosed:
    96  		return
    97  	}
    98  	// Perform job running.
    99  	entry.Run()
   100  }
   101  
   102  // SetStatus custom sets the status for the job.
   103  func (entry *Entry) SetStatus(status int) int {
   104  	return entry.status.Set(status)
   105  }
   106  
   107  // Start starts the job.
   108  func (entry *Entry) Start() {
   109  	entry.status.Set(StatusReady)
   110  }
   111  
   112  // Stop stops the job.
   113  func (entry *Entry) Stop() {
   114  	entry.status.Set(StatusStopped)
   115  }
   116  
   117  // Close closes the job, and then it will be removed from the timer.
   118  func (entry *Entry) Close() {
   119  	entry.status.Set(StatusClosed)
   120  }
   121  
   122  // Reset resets the job, which resets its ticks for next running.
   123  func (entry *Entry) Reset() {
   124  	entry.nextTicks.Set(entry.timer.ticks.Val() + entry.ticks)
   125  }
   126  
   127  // IsSingleton checks and returns whether the job in singleton mode.
   128  func (entry *Entry) IsSingleton() bool {
   129  	return entry.isSingleton.Val()
   130  }
   131  
   132  // SetSingleton sets the job singleton mode.
   133  func (entry *Entry) SetSingleton(enabled bool) {
   134  	entry.isSingleton.Set(enabled)
   135  }
   136  
   137  // Job returns the job function of this job.
   138  func (entry *Entry) Job() JobFunc {
   139  	return entry.job
   140  }
   141  
   142  // Ctx returns the initialized context of this job.
   143  func (entry *Entry) Ctx() context.Context {
   144  	return entry.ctx
   145  }
   146  
   147  // SetTimes sets the limit running times for the job.
   148  func (entry *Entry) SetTimes(times int) {
   149  	entry.times.Set(times)
   150  	entry.infinite.Set(false)
   151  }