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.

232 lines
4.9 KiB

9 years ago
9 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
9 years ago
7 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
9 years ago
7 years ago
7 years ago
9 years ago
  1. package common
  2. import (
  3. "sync"
  4. "time"
  5. )
  6. // Used by RepeatTimer the first time,
  7. // and every time it's Reset() after Stop().
  8. type TickerMaker func(dur time.Duration) Ticker
  9. // Ticker is a basic ticker interface.
  10. type Ticker interface {
  11. // Never changes, never closes.
  12. Chan() <-chan time.Time
  13. // Stopping a stopped Ticker will panic.
  14. Stop()
  15. }
  16. //----------------------------------------
  17. // defaultTicker
  18. var _ Ticker = (*defaultTicker)(nil)
  19. type defaultTicker time.Ticker
  20. func defaultTickerMaker(dur time.Duration) Ticker {
  21. ticker := time.NewTicker(dur)
  22. return (*defaultTicker)(ticker)
  23. }
  24. // Implements Ticker
  25. func (t *defaultTicker) Chan() <-chan time.Time {
  26. return t.C
  27. }
  28. // Implements Ticker
  29. func (t *defaultTicker) Stop() {
  30. ((*time.Ticker)(t)).Stop()
  31. }
  32. //----------------------------------------
  33. // LogicalTickerMaker
  34. // Construct a TickerMaker that always uses `source`.
  35. // It's useful for simulating a deterministic clock.
  36. func NewLogicalTickerMaker(source chan time.Time) TickerMaker {
  37. return func(dur time.Duration) Ticker {
  38. return newLogicalTicker(source, dur)
  39. }
  40. }
  41. type logicalTicker struct {
  42. source <-chan time.Time
  43. ch chan time.Time
  44. quit chan struct{}
  45. }
  46. func newLogicalTicker(source <-chan time.Time, interval time.Duration) Ticker {
  47. lt := &logicalTicker{
  48. source: source,
  49. ch: make(chan time.Time),
  50. quit: make(chan struct{}),
  51. }
  52. go lt.fireRoutine(interval)
  53. return lt
  54. }
  55. // We need a goroutine to read times from t.source
  56. // and fire on t.Chan() when `interval` has passed.
  57. func (t *logicalTicker) fireRoutine(interval time.Duration) {
  58. source := t.source
  59. // Init `lasttime`
  60. lasttime := time.Time{}
  61. select {
  62. case lasttime = <-source:
  63. case <-t.quit:
  64. return
  65. }
  66. // Init `lasttime` end
  67. for {
  68. select {
  69. case newtime := <-source:
  70. elapsed := newtime.Sub(lasttime)
  71. if interval <= elapsed {
  72. // Block for determinism until the ticker is stopped.
  73. select {
  74. case t.ch <- newtime:
  75. case <-t.quit:
  76. return
  77. }
  78. // Reset timeleft.
  79. // Don't try to "catch up" by sending more.
  80. // "Ticker adjusts the intervals or drops ticks to make up for
  81. // slow receivers" - https://golang.org/pkg/time/#Ticker
  82. lasttime = newtime
  83. }
  84. case <-t.quit:
  85. return // done
  86. }
  87. }
  88. }
  89. // Implements Ticker
  90. func (t *logicalTicker) Chan() <-chan time.Time {
  91. return t.ch // immutable
  92. }
  93. // Implements Ticker
  94. func (t *logicalTicker) Stop() {
  95. close(t.quit) // it *should* panic when stopped twice.
  96. }
  97. //---------------------------------------------------------------------
  98. /*
  99. RepeatTimer repeatedly sends a struct{}{} to `.Chan()` after each `dur`
  100. period. (It's good for keeping connections alive.)
  101. A RepeatTimer must be stopped, or it will keep a goroutine alive.
  102. */
  103. type RepeatTimer struct {
  104. name string
  105. ch chan time.Time
  106. tm TickerMaker
  107. mtx sync.Mutex
  108. dur time.Duration
  109. ticker Ticker
  110. quit chan struct{}
  111. }
  112. // NewRepeatTimer returns a RepeatTimer with a defaultTicker.
  113. func NewRepeatTimer(name string, dur time.Duration) *RepeatTimer {
  114. return NewRepeatTimerWithTickerMaker(name, dur, defaultTickerMaker)
  115. }
  116. // NewRepeatTimerWithTicker returns a RepeatTimer with the given ticker
  117. // maker.
  118. func NewRepeatTimerWithTickerMaker(name string, dur time.Duration, tm TickerMaker) *RepeatTimer {
  119. var t = &RepeatTimer{
  120. name: name,
  121. ch: make(chan time.Time),
  122. tm: tm,
  123. dur: dur,
  124. ticker: nil,
  125. quit: nil,
  126. }
  127. t.reset()
  128. return t
  129. }
  130. // receive ticks on ch, send out on t.ch
  131. func (t *RepeatTimer) fireRoutine(ch <-chan time.Time, quit <-chan struct{}) {
  132. for {
  133. select {
  134. case tick := <-ch:
  135. select {
  136. case t.ch <- tick:
  137. case <-quit:
  138. return
  139. }
  140. case <-quit: // NOTE: `t.quit` races.
  141. return
  142. }
  143. }
  144. }
  145. func (t *RepeatTimer) Chan() <-chan time.Time {
  146. return t.ch
  147. }
  148. func (t *RepeatTimer) Stop() {
  149. t.mtx.Lock()
  150. defer t.mtx.Unlock()
  151. t.stop()
  152. }
  153. // Wait the duration again before firing.
  154. func (t *RepeatTimer) Reset() {
  155. t.mtx.Lock()
  156. defer t.mtx.Unlock()
  157. t.reset()
  158. }
  159. //----------------------------------------
  160. // Misc.
  161. // CONTRACT: (non-constructor) caller should hold t.mtx.
  162. func (t *RepeatTimer) reset() {
  163. if t.ticker != nil {
  164. t.stop()
  165. }
  166. t.ticker = t.tm(t.dur)
  167. t.quit = make(chan struct{})
  168. go t.fireRoutine(t.ticker.Chan(), t.quit)
  169. }
  170. // CONTRACT: caller should hold t.mtx.
  171. func (t *RepeatTimer) stop() {
  172. if t.ticker == nil {
  173. /*
  174. Similar to the case of closing channels twice:
  175. https://groups.google.com/forum/#!topic/golang-nuts/rhxMiNmRAPk
  176. Stopping a RepeatTimer twice implies that you do
  177. not know whether you are done or not.
  178. If you're calling stop on a stopped RepeatTimer,
  179. you probably have race conditions.
  180. */
  181. panic("Tried to stop a stopped RepeatTimer")
  182. }
  183. t.ticker.Stop()
  184. t.ticker = nil
  185. /*
  186. From https://golang.org/pkg/time/#Ticker:
  187. "Stop the ticker to release associated resources"
  188. "After Stop, no more ticks will be sent"
  189. So we shouldn't have to do the below.
  190. select {
  191. case <-t.ch:
  192. // read off channel if there's anything there
  193. default:
  194. }
  195. */
  196. close(t.quit)
  197. }