DEV Community

Yi Zhang
Yi Zhang

Posted on

How Tendermint Consensus Timeout Ticker works

In file "tendermint/consensus/ticker.go"

 type TimeoutTicker interface { Start() error Stop() error Chan() <-chan timeoutInfo // on which to receive a timeout ScheduleTimeout(ti timeoutInfo) // reset the timer SetLogger(log.Logger) } type timeoutTicker struct { service.BaseService timer *time.Timer tickChan chan timeoutInfo // for scheduling timeouts tockChan chan timeoutInfo // for notifying about them } func NewTimeoutTicker() TimeoutTicker { tt := &timeoutTicker{ timer: time.NewTimer(0), tickChan: make(chan timeoutInfo, tickTockBufferSize), tockChan: make(chan timeoutInfo, tickTockBufferSize), } tt.BaseService = *service.NewBaseService(nil, "TimeoutTicker", tt) tt.stopTimer() // don't want to fire until the first scheduled timeout return tt } func (t *timeoutTicker) OnStart() error { go t.timeoutRoutine() return nil } func (t *timeoutTicker) OnStop() { t.BaseService.OnStop() t.stopTimer() } func (t *timeoutTicker) Chan() <-chan timeoutInfo { return t.tockChan } func (t *timeoutTicker) ScheduleTimeout(ti timeoutInfo) { t.tickChan <- ti } func (t *timeoutTicker) stopTimer() { // Stop() returns false if it was already fired or was stopped if !t.timer.Stop() { select { case <-t.timer.C: default: t.Logger.Debug("Timer already stopped") } } } func (t *timeoutTicker) timeoutRoutine() { t.Logger.Debug("Starting timeout routine") var ti timeoutInfo for { select { case newti := <-t.tickChan: t.Logger.Debug("Received tick", "old_ti", ti, "new_ti", newti) // ignore tickers for old height/round/step if newti.Height < ti.Height { continue } else if newti.Height == ti.Height { if newti.Round < ti.Round { continue } else if newti.Round == ti.Round { if ti.Step > 0 && newti.Step <= ti.Step { continue } } } // stop the last timer t.stopTimer() ti = newti t.timer.Reset(ti.Duration) t.Logger.Debug("Scheduled timeout", "dur", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) case <-t.timer.C: t.Logger.Info("Timed out", "dur", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) go func(toi timeoutInfo) { t.tockChan <- toi }(ti) case <-t.Quit(): return } } } 
Enter fullscreen mode Exit fullscreen mode

Conclusion:

timeouTicker is a service which we can start and stop. When started it runs a for loop to continuously and blockingly receive from 2 channels: "t.tickerChan" and "t.timer.C".

Basically the timeoutTiker allows caller to call "ScheduleTimeout(ti timeoutInfo)". The timeoutTicker will setup a timer to fire. When firing, the "timeoutInfo ti" will be available at the tockChan channel which caller can get by calling "timoutTicker.Chan()".

Top comments (0)