package scheduler import ( "context" "cycle-scheduler/internal/job" "sync" "sync/atomic" "time" "github.com/google/uuid" ) // atomicTimer wraps a `time.Timer`. type atomicTimer struct { atomic.Pointer[time.Timer] } func (at *atomicTimer) stop() { timer := at.Load() if timer != nil { timer.Stop() } } // set replaces the current timer. // It also ensures that the current timer is stopped. func (at *atomicTimer) set(t *time.Timer) { timer := at.Load() if timer != nil { timer.Stop() at.Swap(t) return } at.Swap(t) } type TaskDetails struct { job.JobDetails Attempts int `json:"attempts"` } type task struct { *job.Job attempts atomic.Uint32 timer atomicTimer } func newTask(f job.FnJob) *task { j := job.NewJob(f) t := task{ Job: &j, timer: atomicTimer{}, } return &t } func (t *task) abort() { 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()), } } type tasks struct { l sync.RWMutex s map[uuid.UUID]*task } func newTasks() tasks { return tasks{ s: make(map[uuid.UUID]*task), } } func (ts *tasks) add(t *task) { ts.l.Lock() defer ts.l.Unlock() ts.s[t.GetID()] = t } func (ts *tasks) get(id uuid.UUID) *task { ts.l.RLock() defer ts.l.RUnlock() j, ok := ts.s[id] if !ok { return nil } return j } func (ts *tasks) len() int { ts.l.RLock() defer ts.l.RUnlock() return len(ts.s) } func (ts *tasks) completed() bool { ts.l.RLock() defer ts.l.RUnlock() for _, t := range ts.s { if t.GetState() == job.Pending || t.GetState() == job.Running { return false } } return true } func (ts *tasks) getAllDetails() []TaskDetails { ts.l.RLock() defer ts.l.RUnlock() details := []TaskDetails{} for _, t := range ts.s { details = append(details, t.getDetails()) } return details } func (ts *tasks) getDetails(id uuid.UUID) TaskDetails { ts.l.RLock() defer ts.l.RUnlock() t, ok := ts.s[id] if !ok { return TaskDetails{ JobDetails: job.JobDetails{ State: job.UnknownState, }, } } return t.getDetails() }