package cleaners import ( "sync" "time" ) type CleanerManager struct { activeCleaners map[string]bool activeCleanerLock sync.Mutex exitChans []chan any } var cleanerBuilders = []func() (onTick func(time.Time), name string, tickSpeed time.Duration){} func NewManager() *CleanerManager { activeCleaners := make(map[string]bool) exitChans := []chan any{} cm := &CleanerManager{ activeCleaners: activeCleaners, exitChans: exitChans, } // Launch all cleaner tickers in a new goroutine each for _, builder := range cleanerBuilders { exitChan := make(chan any, 1) onTick, name, tickSpeed := builder() cm.exitChans = append(cm.exitChans, exitChan) go cm.tickOrExit(tickSpeed, name, exitChan, onTick) } return cm } func (m *CleanerManager) Stop() { for _, exitChan := range m.exitChans { exitChan <- 1 } } func (m *CleanerManager) tickOrExit( tickSpeed time.Duration, name string, exitChan chan any, onTick func(time.Time), ) { ticker := time.Tick(tickSpeed) for { select { case now := <-ticker: go m.wrapOnTick(name, now, onTick) case <-exitChan: return } } } func (m *CleanerManager) wrapOnTick(name string, now time.Time, onTick func(time.Time)) { m.activeCleanerLock.Lock() if m.activeCleaners[name] { m.activeCleanerLock.Unlock() return } m.activeCleaners[name] = true m.activeCleanerLock.Unlock() onTick(now) m.activeCleanerLock.Lock() m.activeCleaners[name] = false m.activeCleanerLock.Unlock() }