You've already forked agentic-coding-workflow
77 lines
1.8 KiB
Go
77 lines
1.8 KiB
Go
package jobs
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Scheduler drives a set of named periodic runners. Each runner is a closure
|
|
// invoked at a fixed interval; panics are recovered and logged.
|
|
type Scheduler struct {
|
|
log *slog.Logger
|
|
entries []schedulerEntry
|
|
wg sync.WaitGroup
|
|
}
|
|
|
|
type schedulerEntry struct {
|
|
name string
|
|
interval time.Duration
|
|
enabled bool
|
|
fn func(context.Context)
|
|
}
|
|
|
|
// NewScheduler builds a Scheduler. Add entries before calling Start.
|
|
func NewScheduler(log *slog.Logger) *Scheduler {
|
|
return &Scheduler{log: log}
|
|
}
|
|
|
|
// Add registers a periodic runner. enabled=false skips it at Start.
|
|
func (s *Scheduler) Add(name string, interval time.Duration, enabled bool, fn func(context.Context)) {
|
|
s.entries = append(s.entries, schedulerEntry{name: name, interval: interval, enabled: enabled, fn: fn})
|
|
}
|
|
|
|
// Start launches one goroutine per enabled entry. Each goroutine exits when
|
|
// ctx is canceled. Wait blocks until all goroutines exit.
|
|
func (s *Scheduler) Start(ctx context.Context) {
|
|
for _, e := range s.entries {
|
|
if !e.enabled || e.interval <= 0 {
|
|
continue
|
|
}
|
|
s.wg.Add(1)
|
|
go s.run(ctx, e)
|
|
}
|
|
}
|
|
|
|
// Wait blocks until Start's goroutines exit (typically after ctx cancel).
|
|
func (s *Scheduler) Wait() { s.wg.Wait() }
|
|
|
|
func (s *Scheduler) run(ctx context.Context, e schedulerEntry) {
|
|
defer s.wg.Done()
|
|
t := time.NewTicker(e.interval)
|
|
defer t.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-t.C:
|
|
s.invoke(ctx, e)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Scheduler) invoke(ctx context.Context, e schedulerEntry) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
s.log.Error("runner.panic", "name", e.name, "panic", r)
|
|
}
|
|
}()
|
|
start := time.Now()
|
|
e.fn(ctx)
|
|
s.log.Info("runner.tick",
|
|
"name", e.name,
|
|
"duration_ms", time.Since(start).Milliseconds(),
|
|
)
|
|
}
|