intervalrunner.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. package interval
  2. import (
  3. "time"
  4. "github.com/opencost/opencost/pkg/util/atomic"
  5. )
  6. // IntervalRunner is an example implementation of AtomicRunState.
  7. type IntervalRunner struct {
  8. runState atomic.AtomicRunState
  9. action func()
  10. interval time.Duration
  11. }
  12. // NewIntervalRunner Creates a new instance of an interval runner to execute the provided
  13. // function on a designated interval until explicitly stopped.
  14. func NewIntervalRunner(action func(), interval time.Duration) *IntervalRunner {
  15. return &IntervalRunner{
  16. action: action,
  17. interval: interval,
  18. }
  19. }
  20. // Start begins the interval execution. It returns true if the interval execution successfully starts.
  21. // It will return false if the interval execcution is already running.
  22. func (ir *IntervalRunner) Start() bool {
  23. // Before we attempt to start, we must ensure we are not in a stopping state, this is a common
  24. // pattern that should be used with the AtomicRunState
  25. ir.runState.WaitForReset()
  26. // This will atomically check the current state to ensure we can run, then advances the state.
  27. // If the state is already started, it will return false.
  28. if !ir.runState.Start() {
  29. return false
  30. }
  31. // our run state is advanced, let's execute our action on the interval
  32. // spawn a new goroutine which will loop and wait the interval each iteration
  33. go func() {
  34. ticker := time.NewTicker(ir.interval)
  35. for {
  36. // use a select statement to receive whichever channel receives data first
  37. select {
  38. // if our stop channel receives data, it means we have explicitly called
  39. // Stop(), and must reset our AtomicRunState to it's initial idle state
  40. case <-ir.runState.OnStop():
  41. ticker.Stop()
  42. ir.runState.Reset()
  43. return // exit go routine
  44. // After our interval elapses, fall through
  45. case <-ticker.C:
  46. }
  47. // Execute the function
  48. ir.action()
  49. // Loop back to the select where we will wait for the interval to elapse
  50. // or an explicit stop to be called
  51. }
  52. }()
  53. return true
  54. }
  55. // Stop will explicitly stop the execution of the interval runner. If an action is already executing, it will wait
  56. // until completion before processing the stop. Any attempts to start during the stopping phase will block until
  57. // it's possible to Start() again
  58. func (ir *IntervalRunner) Stop() bool {
  59. return ir.runState.Stop()
  60. }