package task import ( "context" "sync" "time" ) var globalCtx, globalCancel = context.WithCancel(context.Background()) // A Task is a wrapper around a function that offers scheduling // and syncronization. type Task struct { mutex sync.Mutex ctx context.Context ctxCancel context.CancelFunc waitDuration time.Duration scheduled bool callback func() error } // Context gets the task's context. func (task *Task) Context() context.Context { return task.ctx } // Stop stops a task. This should not be done to stop a single run, // but as a way to cancel it forever. func (task *Task) Stop() { task.ctxCancel() } // Schedule schedules a task for later execution. func (task *Task) Schedule() { // Don't if the context is closed. select { case <-task.Context().Done(): return default: } task.mutex.Lock() if task.scheduled { task.mutex.Unlock() return } task.scheduled = true task.mutex.Unlock() go func() { <-time.After(task.waitDuration) // Mark as no longer scheduled task.mutex.Lock() task.scheduled = false task.mutex.Unlock() // Don't if the task is cancelled select { case <-task.Context().Done(): return default: } task.callback() }() } // New makes a new task with the callback. func New(waitDuration time.Duration, callback func() error) *Task { ctx, ctxCancel := context.WithCancel(globalCtx) return &Task{ callback: callback, ctx: ctx, ctxCancel: ctxCancel, waitDuration: waitDuration, } } // StopAll stops all tasks. func StopAll() { globalCancel() }