github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/autoprofile/internal/timer.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2020 3 4 package internal 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/instana/go-sensor/autoprofile/internal/logger" 11 ) 12 13 // Timer periodically executes provided job after a delay until it's stopped. Any panic 14 // occurred inside the job is recovered and logged 15 type Timer struct { 16 mu sync.Mutex 17 delayTimer *time.Timer 18 done chan bool 19 stopped bool 20 ticker *time.Ticker 21 } 22 23 // NewTimer initializes a new Timer 24 func NewTimer(delay, interval time.Duration, job func()) *Timer { 25 t := &Timer{ 26 done: make(chan bool), 27 } 28 29 t.delayTimer = time.AfterFunc(delay, func() { 30 defer recoverAndLog() 31 32 if interval > 0 { 33 t.runTicker(interval, job) 34 } 35 36 if delay > 0 { 37 job() 38 } 39 }) 40 41 return t 42 } 43 44 func (t *Timer) runTicker(interval time.Duration, job func()) { 45 t.mu.Lock() 46 defer t.mu.Unlock() 47 48 if t.stopped { 49 return 50 } 51 52 t.ticker = time.NewTicker(interval) 53 go func() { 54 defer recoverAndLog() 55 56 for { 57 select { 58 case <-t.ticker.C: 59 job() 60 case <-t.done: 61 return 62 } 63 } 64 }() 65 } 66 67 // Stop stops the job execution 68 func (t *Timer) Stop() { 69 if t.stopped { 70 return 71 } 72 73 t.mu.Lock() 74 defer t.mu.Unlock() 75 76 t.stopped = true 77 t.delayTimer.Stop() 78 79 close(t.done) 80 81 if t.ticker != nil { 82 t.ticker.Stop() 83 } 84 } 85 86 func recoverAndLog() { 87 if err := recover(); err != nil { 88 logger.Error("recovered from panic in agent:", err) 89 } 90 }