OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / tendermint / tmlibs / common / repeat_timer.go
1 package common
2
3 import (
4         "sync"
5         "time"
6 )
7
8 /*
9 RepeatTimer repeatedly sends a struct{}{} to .Ch after each "dur" period.
10 It's good for keeping connections alive.
11 A RepeatTimer must be Stop()'d or it will keep a goroutine alive.
12 */
13 type RepeatTimer struct {
14         Ch chan time.Time
15
16         mtx    sync.Mutex
17         name   string
18         ticker *time.Ticker
19         quit   chan struct{}
20         wg     *sync.WaitGroup
21         dur    time.Duration
22 }
23
24 func NewRepeatTimer(name string, dur time.Duration) *RepeatTimer {
25         var t = &RepeatTimer{
26                 Ch:     make(chan time.Time),
27                 ticker: time.NewTicker(dur),
28                 quit:   make(chan struct{}),
29                 wg:     new(sync.WaitGroup),
30                 name:   name,
31                 dur:    dur,
32         }
33         t.wg.Add(1)
34         go t.fireRoutine(t.ticker)
35         return t
36 }
37
38 func (t *RepeatTimer) fireRoutine(ticker *time.Ticker) {
39         for {
40                 select {
41                 case t_ := <-ticker.C:
42                         t.Ch <- t_
43                 case <-t.quit:
44                         // needed so we know when we can reset t.quit
45                         t.wg.Done()
46                         return
47                 }
48         }
49 }
50
51 // Wait the duration again before firing.
52 func (t *RepeatTimer) Reset() {
53         t.Stop()
54
55         t.mtx.Lock() // Lock
56         defer t.mtx.Unlock()
57
58         t.ticker = time.NewTicker(t.dur)
59         t.quit = make(chan struct{})
60         t.wg.Add(1)
61         go t.fireRoutine(t.ticker)
62 }
63
64 // For ease of .Stop()'ing services before .Start()'ing them,
65 // we ignore .Stop()'s on nil RepeatTimers.
66 func (t *RepeatTimer) Stop() bool {
67         if t == nil {
68                 return false
69         }
70         t.mtx.Lock() // Lock
71         defer t.mtx.Unlock()
72
73         exists := t.ticker != nil
74         if exists {
75                 t.ticker.Stop() // does not close the channel
76                 select {
77                 case <-t.Ch:
78                         // read off channel if there's anything there
79                 default:
80                 }
81                 close(t.quit)
82                 t.wg.Wait() // must wait for quit to close else we race Reset
83                 t.ticker = nil
84         }
85         return exists
86 }