package scheduler import ( "context" "cycle-scheduler/internal/job" "sync" "sync/atomic" "time" ) // safeTime wraps a `time.Timer` with a lock to be thread safe. type safeTimer struct { l sync.Mutex timer *time.Timer } func (st *safeTimer) stop() { if st.timer != nil { st.timer.Stop() } } // set replaces the current timer. // It also ensures that the current timer is stopped. func (st *safeTimer) set(t *time.Timer) { st.l.Lock() defer st.l.Unlock() if st.timer != nil { st.timer.Stop() st.timer = t return } st.timer = t } type TaskDetails struct { job.JobDetails Attempts int `json:"attempts"` } type task struct { *job.Job attempts atomic.Uint32 timer *safeTimer } func newTask(f job.FnJob) *task { j := job.NewJob(f) t := task{ Job: &j, timer: &safeTimer{}, } return &t } func (t *task) abort() { if t.timer != nil { t.timer.stop() } t.Job.Abort() } func (t *task) run(ctx context.Context) { t.attempts.Add(1) t.Job.Run(ctx) } func (t *task) getDetails() TaskDetails { return TaskDetails{ JobDetails: t.IntoDetails(), Attempts: int(t.attempts.Load()), } }