Files
traefikoidc/autocleanup.go
T

131 lines
3.3 KiB
Go

package traefikoidc
import (
"sync"
"time"
)
// BackgroundTask provides a robust framework for running periodic background tasks
// with proper lifecycle management, graceful shutdown, and logging capabilities.
// It supports both internal and external WaitGroup coordination for complex cleanup scenarios.
type BackgroundTask struct {
stopChan chan struct{}
taskFunc func()
logger *Logger
externalWG *sync.WaitGroup
name string
internalWG sync.WaitGroup
interval time.Duration
stopOnce sync.Once
mu sync.Mutex
stopped bool
}
// NewBackgroundTask creates a new background task with the specified configuration.
// The task will execute taskFunc immediately when started, then at the specified interval.
// Parameters:
// - name: Human-readable name for the task (used in logging)
// - interval: How often to execute the task function
// - taskFunc: The function to execute periodically
// - logger: Logger for task events (can be nil)
// - wg: Optional external WaitGroup for coordinated shutdown
//
// Returns:
// - A configured BackgroundTask ready to be started
func NewBackgroundTask(name string, interval time.Duration, taskFunc func(), logger *Logger, wg ...*sync.WaitGroup) *BackgroundTask {
var externalWG *sync.WaitGroup
if len(wg) > 0 {
externalWG = wg[0]
}
return &BackgroundTask{
name: name,
interval: interval,
stopChan: make(chan struct{}),
taskFunc: taskFunc,
logger: logger,
externalWG: externalWG,
}
}
// Start begins executing the background task in a separate goroutine.
// The task function is executed immediately, then at the configured interval.
// The task runs immediately upon start and then at the specified interval.
func (bt *BackgroundTask) Start() {
bt.mu.Lock()
defer bt.mu.Unlock()
if bt.stopped {
if bt.logger != nil {
bt.logger.Infof("Attempted to start already stopped task: %s", bt.name)
}
return
}
bt.internalWG.Add(1)
if bt.externalWG != nil {
bt.externalWG.Add(1)
}
go bt.run()
}
// Stop gracefully shuts down the background task and waits for completion.
// It signals the task to stop and waits for the goroutine to finish.
// This method is safe to call multiple times.
func (bt *BackgroundTask) Stop() {
bt.stopOnce.Do(func() {
bt.mu.Lock()
bt.stopped = true
bt.mu.Unlock()
close(bt.stopChan)
bt.internalWG.Wait()
})
}
// run is the main loop for the background task.
// It executes the task function immediately, then periodically
// until the stop signal is received.
func (bt *BackgroundTask) run() {
defer func() {
bt.internalWG.Done()
if bt.externalWG != nil {
bt.externalWG.Done()
}
}()
ticker := time.NewTicker(bt.interval)
defer ticker.Stop()
if bt.logger != nil {
bt.logger.Info("Starting background task: %s", bt.name)
}
bt.taskFunc()
for {
select {
case <-ticker.C:
bt.taskFunc()
case <-bt.stopChan:
if bt.logger != nil {
bt.logger.Info("Stopping background task: %s", bt.name)
}
return
}
}
}
// autoCleanupRoutine is a legacy function for running periodic cleanup tasks.
// Deprecated: Use BackgroundTask instead for better lifecycle management and logging.
func autoCleanupRoutine(interval time.Duration, stop <-chan struct{}, cleanup func()) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
cleanup()
case <-stop:
return
}
}
}