You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

102 lines
2.1 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package common
  2. import (
  3. "time"
  4. )
  5. /*
  6. RepeatTimer repeatedly sends a struct{}{} to .Ch after each "dur" period.
  7. It's good for keeping connections alive.
  8. A RepeatTimer must be Stop()'d or it will keep a goroutine alive.
  9. */
  10. type RepeatTimer struct {
  11. Name string
  12. Ch <-chan time.Time
  13. output chan<- time.Time
  14. input chan repeatCommand
  15. dur time.Duration
  16. ticker *time.Ticker
  17. stopped bool
  18. }
  19. type repeatCommand int8
  20. const (
  21. Reset repeatCommand = iota
  22. RQuit
  23. )
  24. func NewRepeatTimer(name string, dur time.Duration) *RepeatTimer {
  25. c := make(chan time.Time)
  26. var t = &RepeatTimer{
  27. Name: name,
  28. Ch: c,
  29. output: c,
  30. input: make(chan repeatCommand),
  31. dur: dur,
  32. ticker: time.NewTicker(dur),
  33. }
  34. go t.run()
  35. return t
  36. }
  37. // Wait the duration again before firing.
  38. func (t *RepeatTimer) Reset() {
  39. t.input <- Reset
  40. }
  41. // For ease of .Stop()'ing services before .Start()'ing them,
  42. // we ignore .Stop()'s on nil RepeatTimers.
  43. func (t *RepeatTimer) Stop() bool {
  44. // use t.stopped to gracefully handle many Stop() without blocking
  45. if t == nil || t.stopped {
  46. return false
  47. }
  48. t.input <- RQuit
  49. t.stopped = true
  50. return true
  51. }
  52. func (t *RepeatTimer) run() {
  53. done := false
  54. for !done {
  55. select {
  56. case cmd := <-t.input:
  57. // stop goroutine if the input says so
  58. // don't close channels, as closed channels mess up select reads
  59. done = t.processInput(cmd)
  60. case tick := <-t.ticker.C:
  61. t.send(tick)
  62. }
  63. }
  64. }
  65. // send performs blocking send on t.Ch
  66. func (t *RepeatTimer) send(tick time.Time) {
  67. // XXX: possibly it is better to not block:
  68. // https://golang.org/src/time/sleep.go#L132
  69. // select {
  70. // case t.output <- tick:
  71. // default:
  72. // }
  73. t.output <- tick
  74. }
  75. // all modifications of the internal state of ThrottleTimer
  76. // happen in this method. It is only called from the run goroutine
  77. // so we avoid any race conditions
  78. func (t *RepeatTimer) processInput(cmd repeatCommand) (shutdown bool) {
  79. switch cmd {
  80. case Reset:
  81. t.ticker.Stop()
  82. t.ticker = time.NewTicker(t.dur)
  83. case RQuit:
  84. t.ticker.Stop()
  85. shutdown = true
  86. default:
  87. panic("unknown command!")
  88. }
  89. return shutdown
  90. }