github.com/mier85/go-sensor@v1.30.1-0.20220920111756-9bf41b3bc7e0/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/mier85/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  }