github.com/okex/exchain@v1.8.0/libs/tendermint/consensus/ticker.go (about) 1 package consensus 2 3 import ( 4 "time" 5 6 "github.com/okex/exchain/libs/tendermint/libs/log" 7 "github.com/okex/exchain/libs/tendermint/libs/service" 8 ) 9 10 var ( 11 tickTockBufferSize = 10 12 ) 13 14 // TimeoutTicker is a timer that schedules timeouts 15 // conditional on the height/round/step in the timeoutInfo. 16 // The timeoutInfo.Duration may be non-positive. 17 type TimeoutTicker interface { 18 Start() error 19 Stop() error 20 Reset() error 21 Chan() <-chan timeoutInfo // on which to receive a timeout 22 ScheduleTimeout(ti timeoutInfo) // reset the timer 23 24 SetLogger(log.Logger) 25 } 26 27 // timeoutTicker wraps time.Timer, 28 // scheduling timeouts only for greater height/round/step 29 // than what it's already seen. 30 // Timeouts are scheduled along the tickChan, 31 // and fired on the tockChan. 32 type timeoutTicker struct { 33 service.BaseService 34 35 timer *time.Timer 36 tickChan chan timeoutInfo // for scheduling timeouts 37 tockChan chan timeoutInfo // for notifying about them 38 } 39 40 // NewTimeoutTicker returns a new TimeoutTicker. 41 func NewTimeoutTicker() TimeoutTicker { 42 tt := &timeoutTicker{ 43 timer: time.NewTimer(0), 44 tickChan: make(chan timeoutInfo, tickTockBufferSize), 45 tockChan: make(chan timeoutInfo, tickTockBufferSize), 46 } 47 tt.BaseService = *service.NewBaseService(nil, "TimeoutTicker", tt) 48 tt.stopTimer() // don't want to fire until the first scheduled timeout 49 return tt 50 } 51 52 // OnStart implements service.Service. It starts the timeout routine. 53 func (t *timeoutTicker) OnStart() error { 54 55 go t.timeoutRoutine() 56 57 return nil 58 } 59 60 // OnStop implements service.Service. It stops the timeout routine. 61 func (t *timeoutTicker) OnStop() { 62 t.BaseService.OnStop() 63 t.stopTimer() 64 } 65 66 // OnReset implements service.Service. It resets the timeout routine. 67 func (t *timeoutTicker) OnReset() error { 68 return nil 69 } 70 71 // Chan returns a channel on which timeouts are sent. 72 func (t *timeoutTicker) Chan() <-chan timeoutInfo { 73 return t.tockChan 74 } 75 76 // ScheduleTimeout schedules a new timeout by sending on the internal tickChan. 77 // The timeoutRoutine is always available to read from tickChan, so this won't block. 78 // The scheduling may fail if the timeoutRoutine has already scheduled a timeout for a later height/round/step. 79 func (t *timeoutTicker) ScheduleTimeout(ti timeoutInfo) { 80 t.tickChan <- ti 81 } 82 83 //------------------------------------------------------------- 84 85 // stop the timer and drain if necessary 86 func (t *timeoutTicker) stopTimer() { 87 // Stop() returns false if it was already fired or was stopped 88 if !t.timer.Stop() { 89 select { 90 case <-t.timer.C: 91 default: 92 t.Logger.Debug("Timer already stopped") 93 } 94 } 95 } 96 97 // send on tickChan to start a new timer. 98 // timers are interupted and replaced by new ticks from later steps 99 // timeouts of 0 on the tickChan will be immediately relayed to the tockChan 100 func (t *timeoutTicker) timeoutRoutine() { 101 t.Logger.Debug("Starting timeout routine") 102 var ti timeoutInfo 103 for { 104 select { 105 case newti := <-t.tickChan: 106 t.Logger.Debug("Received tick", "old_ti", ti, "new_ti", newti) 107 108 // ignore tickers for old height/round/step 109 if newti.Height < ti.Height { 110 continue 111 } else if newti.Height == ti.Height { 112 if newti.Round < ti.Round { 113 continue 114 } else if newti.Round == ti.Round { 115 if ti.Step > 0 && newti.Step <= ti.Step && !newti.ActiveViewChange { 116 continue 117 } 118 } 119 } 120 121 // stop the last timer 122 t.stopTimer() 123 124 // update timeoutInfo and reset timer 125 // NOTE time.Timer allows duration to be non-positive 126 ti = newti 127 t.timer.Reset(ti.Duration) 128 t.Logger.Debug("Scheduled timeout", "dur", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) 129 case <-t.timer.C: 130 t.Logger.Info("Timed out", "dur", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) 131 // go routine here guarantees timeoutRoutine doesn't block. 132 // Determinism comes from playback in the receiveRoutine. 133 // We can eliminate it by merging the timeoutRoutine into receiveRoutine 134 // and managing the timeouts ourselves with a millisecond ticker 135 go func(toi timeoutInfo) { t.tockChan <- toi }(ti) 136 case <-t.Quit(): 137 return 138 } 139 } 140 }