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 }