github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/ztime/cron/cron.go (about)

     1  // Package cron emulate linux crontab
     2  package cron
     3  
     4  import (
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/sohaha/zlsgo/zstring"
     9  )
    10  
    11  type (
    12  	// Job cron job
    13  	Job struct {
    14  		expr     *Expression
    15  		NextTime time.Time
    16  		run      func()
    17  	}
    18  	JobTable struct {
    19  		table sync.Map
    20  		sync.RWMutex
    21  		stop bool
    22  	}
    23  )
    24  
    25  func New() *JobTable {
    26  	return &JobTable{}
    27  }
    28  
    29  func (c *JobTable) Add(cronLine string, fn func()) (remove func(), err error) {
    30  	var expr *Expression
    31  	expr, err = Parse(cronLine)
    32  	if err != nil {
    33  		return
    34  	}
    35  	task := &Job{
    36  		expr:     expr,
    37  		run:      fn,
    38  		NextTime: expr.Next(time.Now()),
    39  	}
    40  	key := zstring.UUID()
    41  	c.table.Store(key, task)
    42  	remove = func() {
    43  		c.table.Delete(key)
    44  	}
    45  	return
    46  }
    47  
    48  func (c *JobTable) ForceRun() (nextTime time.Duration) {
    49  	now := time.Now()
    50  	nextTime = 1 * time.Second
    51  	// todo there is a sequence problem
    52  	// todo later optimization directly obtains the next execution time
    53  	c.table.Range(func(key, value interface{}) bool {
    54  		cronjob, ok := value.(*Job)
    55  		if ok {
    56  			if cronjob.NextTime.Before(now) || cronjob.NextTime.Equal(now) {
    57  				go cronjob.run()
    58  				cronjob.NextTime = cronjob.expr.Next(now)
    59  			}
    60  		}
    61  		next := time.Duration(cronjob.NextTime.UnixNano() - now.UnixNano())
    62  		if nextTime > next {
    63  			nextTime = next
    64  		}
    65  		return true
    66  	})
    67  	return nextTime
    68  }
    69  
    70  func (c *JobTable) Run(block ...bool) {
    71  	run := func() {
    72  		t := time.NewTimer(time.Second)
    73  		for {
    74  			c.RLock()
    75  			stop := c.stop
    76  			c.RUnlock()
    77  			if stop {
    78  				break
    79  			}
    80  			NextTime := c.ForceRun()
    81  			t.Reset(NextTime)
    82  			<-t.C
    83  		}
    84  	}
    85  	if len(block) > 0 && block[0] {
    86  		run()
    87  		return
    88  	}
    89  
    90  	go run()
    91  }
    92  
    93  func (c *JobTable) Stop() {
    94  	c.Lock()
    95  	c.stop = true
    96  	c.Unlock()
    97  }