gotosocial/vendor/codeberg.org/gruf/go-sched/job.go
kim f3e2d36d64
[chore] update go-sched pkg (#3357)
* update go-sched to v1.2.4 which removes some now unused dependencies

* whoops, remove test output
2024-09-26 19:23:41 +00:00

143 lines
3.3 KiB
Go

package sched
import (
"reflect"
"strconv"
"strings"
"sync/atomic"
"time"
"unsafe"
)
// Job encapsulates logic for a scheduled job to be run according
// to a set Timing, executing the job with a set panic handler, and
// holding onto a next execution time safely in a concurrent environment.
type Job struct {
id uint64
next unsafe.Pointer // *time.Time
timing Timing
call func(time.Time)
panic func(interface{})
}
// NewJob returns a new Job to run given function.
func NewJob(fn func(now time.Time)) *Job {
if fn == nil {
// Ensure a function
panic("nil func")
}
j := &Job{ // set defaults
timing: emptytiming, // i.e. fire immediately
call: fn,
panic: func(i interface{}) { panic(i) },
}
return j
}
// At sets this Job to execute at time, by passing (*sched.Once)(&at) to .With(). See .With() for details.
func (job *Job) At(at time.Time) *Job {
return job.With((*Once)(&at))
}
// Every sets this Job to execute every period, by passing sched.Period(period) to .With(). See .With() for details.
func (job *Job) Every(period time.Duration) *Job {
return job.With(Periodic(period))
}
// EveryAt sets this Job to execute every period starting at time, by passing &PeriodicAt{once: Once(at), period: Periodic(period)} to .With(). See .With() for details.
func (job *Job) EveryAt(at time.Time, period time.Duration) *Job {
return job.With(&PeriodicAt{Once: Once(at), Period: Periodic(period)})
}
// With sets this Job's timing to given implementation, or if already set will wrap existing using sched.TimingWrap{}.
func (job *Job) With(t Timing) *Job {
if t == nil {
// Ensure a timing
panic("nil Timing")
}
if job.id != 0 {
// Cannot update scheduled job
panic("job already scheduled")
}
if job.timing == emptytiming {
// Set new timing
job.timing = t
} else {
// Wrap old timing
old := job.timing
job.timing = &TimingWrap{
Outer: t,
Inner: old,
}
}
return job
}
// OnPanic specifies how this job handles panics, default is an actual panic.
func (job *Job) OnPanic(fn func(interface{})) *Job {
if fn == nil {
// Ensure a function
panic("nil func")
}
if job.id != 0 {
// Cannot update scheduled job
panic("job already scheduled")
}
job.panic = fn
return job
}
// Next returns the next time this Job is expected to run.
func (job *Job) Next() time.Time {
return loadTime(&job.next)
}
// Run will execute this Job and pass through given now time.
func (job *Job) Run(now time.Time) {
defer func() {
switch r := recover(); {
case r == nil:
// no panic
case job != nil &&
job.panic != nil:
job.panic(r)
default:
panic(r)
}
}()
job.call(now)
}
// String provides a debuggable string representation of Job including ID, next time and Timing type.
func (job *Job) String() string {
var buf strings.Builder
buf.WriteByte('{')
buf.WriteString("id=")
buf.WriteString(strconv.FormatUint(job.id, 10))
buf.WriteByte(' ')
buf.WriteString("next=")
buf.WriteString(loadTime(&job.next).Format(time.StampMicro))
buf.WriteByte(' ')
buf.WriteString("timing=")
buf.WriteString(reflect.TypeOf(job.timing).String())
buf.WriteByte('}')
return buf.String()
}
func loadTime(p *unsafe.Pointer) time.Time {
if p := atomic.LoadPointer(p); p != nil {
return *(*time.Time)(p)
}
return zerotime
}
func storeTime(p *unsafe.Pointer, t time.Time) {
atomic.StorePointer(p, unsafe.Pointer(&t))
}