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.
219 lines
4.0 KiB
219 lines
4.0 KiB
package daemon
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Option configure job.
|
|
type Option func(*Job)
|
|
|
|
// Job run job by frequency.
|
|
type Job struct {
|
|
name string
|
|
run Run
|
|
stop Run
|
|
sem chan struct{}
|
|
err chan error
|
|
timer Timer
|
|
delay time.Duration
|
|
freq func(time.Time) time.Duration
|
|
handleErr func(error)
|
|
}
|
|
|
|
// WithName sets job name.
|
|
func WithName(name string) Option {
|
|
return func(j *Job) { j.name = name }
|
|
}
|
|
|
|
// WithStop sets stop handle for job.
|
|
func WithStop(stop Run) Option {
|
|
return func(j *Job) { j.stop = stop }
|
|
}
|
|
|
|
// WithTimer sets time,r to job.
|
|
func WithTimer(timer Timer) Option {
|
|
return func(j *Job) { j.timer = timer }
|
|
}
|
|
|
|
// WithDelay sets delay Run job.
|
|
func WithDelay(delay time.Duration) Option {
|
|
return func(j *Job) {
|
|
j.timer.Reset(delay)
|
|
j.delay = delay
|
|
}
|
|
}
|
|
|
|
// WithFreq sets frequency Run job..
|
|
func WithFreq(freq time.Duration) Option {
|
|
return func(j *Job) {
|
|
j.freq = func(time.Time) time.Duration {
|
|
return freq
|
|
}
|
|
}
|
|
}
|
|
|
|
// WithSchedule set delay and frequency Run job.
|
|
func WithSchedule(next func(time.Time) time.Duration) Option {
|
|
return func(j *Job) {
|
|
j.freq = next
|
|
j.delay = next(time.Now())
|
|
j.timer.Reset(j.delay)
|
|
}
|
|
}
|
|
|
|
// WithRunMiddleware added middleware for the run job.
|
|
func WithRunMiddleware(fn ...Handle) Option {
|
|
return func(j *Job) {
|
|
run := j.run
|
|
j.run = func(ctx context.Context) error {
|
|
return chain(fn...)(ctx, run)
|
|
}
|
|
}
|
|
}
|
|
|
|
// WithStopMiddleware added middleware for the stop job.
|
|
func WithStopMiddleware(fn ...Handle) Option {
|
|
return func(j *Job) {
|
|
stop := j.stop
|
|
j.stop = func(ctx context.Context) error {
|
|
return chain(fn...)(ctx, stop)
|
|
}
|
|
}
|
|
}
|
|
|
|
// WithHandleErr add error hanler.
|
|
func WithHandleErr(fn func(error)) Option {
|
|
return func(j *Job) {
|
|
j.handleErr = fn
|
|
}
|
|
}
|
|
|
|
// Run init function for the change state.
|
|
type Run func(ctx context.Context) error
|
|
|
|
// Handle middleware interface.
|
|
type Handle func(ctx context.Context, next Run) error
|
|
|
|
func stopJob(ctx context.Context) error {
|
|
return nil
|
|
}
|
|
|
|
func handleErr(error) {}
|
|
|
|
// NewJob creates new job.
|
|
func NewJob(run Run, opts ...Option) *Job {
|
|
j := Job{
|
|
delay: time.Nanosecond,
|
|
sem: make(chan struct{}, 1),
|
|
err: make(chan error, 1),
|
|
timer: NewTicker(time.Nanosecond),
|
|
freq: func(time.Time) time.Duration {
|
|
return time.Second
|
|
},
|
|
name: getFuncName(run),
|
|
stop: stopJob,
|
|
run: run,
|
|
handleErr: handleErr,
|
|
}
|
|
j = j.With(opts...)
|
|
|
|
return &j
|
|
}
|
|
|
|
// HandleErr handle returned error.
|
|
func (j *Job) HandleErr(err error) {
|
|
j.handleErr(err)
|
|
}
|
|
|
|
// Do run job.
|
|
func (j *Job) Do(ctx context.Context) <-chan error {
|
|
<-j.timer.Tick()
|
|
j.sem <- struct{}{}
|
|
err := j.run(ctx)
|
|
<-j.sem
|
|
|
|
switch tr := err.(type) {
|
|
case *stop:
|
|
case *delay:
|
|
j.timer.Reset(tr.d)
|
|
default:
|
|
j.timer.Reset(j.freq(time.Now()))
|
|
}
|
|
j.err <- err
|
|
|
|
return j.err
|
|
}
|
|
|
|
// Stop job.
|
|
func (j *Job) Stop(ctx context.Context) error {
|
|
return j.stop(ctx)
|
|
}
|
|
|
|
// String gets job name.
|
|
func (j *Job) String() string {
|
|
return j.name
|
|
}
|
|
|
|
// With configure job.
|
|
func (j Job) With(opts ...Option) Job {
|
|
for _, o := range opts {
|
|
o(&j)
|
|
}
|
|
|
|
return j
|
|
}
|
|
|
|
func getFuncName(i interface{}) string {
|
|
callerName := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
|
if callerName == "" {
|
|
return "-"
|
|
}
|
|
|
|
callerName = strings.NewReplacer("(*", "", ")", "").Replace(callerName)
|
|
lastIndex := strings.LastIndex(callerName, "/")
|
|
|
|
if lastIndex != -1 {
|
|
callerName = callerName[lastIndex+1:]
|
|
}
|
|
|
|
return callerName
|
|
}
|
|
|
|
func chain(handleFunc ...Handle) Handle {
|
|
n := len(handleFunc)
|
|
if n > 1 {
|
|
lastI := n - 1
|
|
|
|
return func(ctx context.Context, next Run) error {
|
|
var (
|
|
chainHandler Run
|
|
curI int
|
|
)
|
|
|
|
chainHandler = func(currentCtx context.Context) error {
|
|
if curI == lastI {
|
|
return next(currentCtx)
|
|
}
|
|
curI++
|
|
err := handleFunc[curI](currentCtx, chainHandler)
|
|
curI--
|
|
|
|
return err
|
|
}
|
|
|
|
return handleFunc[0](ctx, chainHandler)
|
|
}
|
|
}
|
|
|
|
if n == 1 {
|
|
return handleFunc[0]
|
|
}
|
|
|
|
return func(ctx context.Context, next Run) error {
|
|
return next(ctx)
|
|
}
|
|
}
|
|
|