2
0

atomicrunstate.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. package atomic
  2. import (
  3. "sync"
  4. )
  5. // AtomicRunState can be used to provide thread-safe start/stop functionality to internal run-loops
  6. // inside a goroutine.
  7. type AtomicRunState struct {
  8. lock sync.Mutex
  9. stopping bool
  10. stop chan struct{}
  11. reset chan struct{}
  12. }
  13. // Start checks for an existing run state and returns false if the run state has already started. If
  14. // the run state has not started, then it will advance to the started state and return true.
  15. func (ars *AtomicRunState) Start() bool {
  16. ars.lock.Lock()
  17. defer ars.lock.Unlock()
  18. if ars.stop != nil {
  19. return false
  20. }
  21. ars.stop = make(chan struct{})
  22. return true
  23. }
  24. // OnStop returns a channel that should be used within a select goroutine run loop. It is set to signal
  25. // whenever Stop() is executed. Once the channel is signaled, Reset() should be called if the runstate
  26. // is to be used again.
  27. func (ars *AtomicRunState) OnStop() <-chan struct{} {
  28. ars.lock.Lock()
  29. defer ars.lock.Unlock()
  30. return ars.stop
  31. }
  32. // Stops closes the stop channel triggering any selects waiting for OnStop()
  33. func (ars *AtomicRunState) Stop() bool {
  34. ars.lock.Lock()
  35. defer ars.lock.Unlock()
  36. if !ars.stopping && ars.stop != nil {
  37. ars.stopping = true
  38. ars.reset = make(chan struct{})
  39. close(ars.stop)
  40. return true
  41. }
  42. return false
  43. }
  44. // Reset should be called in the select case for OnStop(). Note that calling Reset() prior to
  45. // selecting OnStop() will result in failed Stop signal receive.
  46. func (ars *AtomicRunState) Reset() {
  47. ars.lock.Lock()
  48. defer ars.lock.Unlock()
  49. close(ars.reset)
  50. ars.stopping = false
  51. ars.stop = nil
  52. }
  53. // IsRunning returns true if the state is running or in the process of stopping.
  54. func (ars *AtomicRunState) IsRunning() bool {
  55. ars.lock.Lock()
  56. defer ars.lock.Unlock()
  57. return ars.stop != nil
  58. }
  59. // IsStopping returns true if the run state has been stopped, but not yet reset.
  60. func (ars *AtomicRunState) IsStopping() bool {
  61. ars.lock.Lock()
  62. defer ars.lock.Unlock()
  63. return ars.stopping && ars.stop != nil
  64. }
  65. // WaitForStop will wait for a stop to occur IFF the run state is in the process of stopping.
  66. func (ars *AtomicRunState) WaitForReset() {
  67. if ars.IsStopping() {
  68. <-ars.reset
  69. }
  70. }