github.com/annchain/OG@v0.0.9/consensus/bft/waiter.go (about) 1 // Copyright © 2019 Annchain Authors <EMAIL ADDRESS> 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package bft 15 16 import ( 17 "github.com/sirupsen/logrus" 18 "time" 19 ) 20 21 type WaiterContext interface { 22 Equal(WaiterContext) bool 23 IsAfter(WaiterContext) bool 24 } 25 26 type WaiterRequest struct { 27 WaitTime time.Duration 28 Context WaiterContext 29 TimeoutCallback func(WaiterContext) 30 } 31 32 // Waiter provides a way to wait for some context to be changed in a certain time. 33 // If the context is not changed, callback function will be triggered. 34 // TODO: replace it with context package 35 type Waiter struct { 36 currentRequest *WaiterRequest 37 requestChannel chan *WaiterRequest 38 contextChannel chan WaiterContext 39 callbackEventChanel chan *WaiterRequest 40 quit chan bool 41 } 42 43 func NewWaiter(callbackEventChannel chan *WaiterRequest) *Waiter { 44 return &Waiter{ 45 requestChannel: make(chan *WaiterRequest, 10), 46 contextChannel: make(chan WaiterContext, 10), 47 quit: make(chan bool), 48 callbackEventChanel: callbackEventChannel, 49 } 50 } 51 52 func (w *Waiter) StartEventLoop() { 53 timer := time.NewTimer(time.Duration(10)) 54 for { 55 select { 56 case <-w.quit: 57 logrus.Info("got quit msg , will stop event loop") 58 return 59 case request := <-w.requestChannel: 60 // could be an updated request 61 // if it is really updated request, 62 if w.currentRequest != nil && !request.Context.IsAfter(w.currentRequest.Context) { 63 // this request is before current waiting request, ignore. 64 continue 65 } 66 logrus.Trace("request is newer and we will reset") 67 w.currentRequest = request 68 if !timer.Stop() { 69 // drain the timer but do not use the method in document 70 // timer may already be consumed so use a select 71 select { 72 case <-timer.C: 73 default: 74 } 75 } 76 timer.Reset(request.WaitTime) 77 case latestContext := <-w.contextChannel: 78 if w.currentRequest == nil || latestContext.IsAfter(w.currentRequest.Context) { 79 // a new state is updated, cancel all pending timeouts 80 if w.currentRequest != nil { 81 logrus.WithField("new", latestContext.(*TendermintContext).StepType). 82 WithField("old", w.currentRequest.Context.(*TendermintContext).StepType). 83 Debug("bft new state updated") 84 } else { 85 logrus.WithField("new", latestContext.(*TendermintContext).StepType). 86 WithField("old", nil). 87 Debug("bft new state updated") 88 } 89 if !timer.Stop() { 90 // drain the timer but do not use the method in document 91 // timer may already be consumed so use a select 92 select { 93 case <-timer.C: 94 default: 95 } 96 } 97 } 98 case <-timer.C: 99 // timeout, trigger callback 100 if w.currentRequest != nil { 101 //ffchan.NewTimeoutSenderShort(w.callbackEventChanel, w.currentRequest, "waiterCallback") 102 w.callbackEventChanel <- w.currentRequest 103 //w.currentRequest.TimeoutCallback(w.currentRequest.Context) 104 } 105 } 106 } 107 } 108 109 func (w *Waiter) UpdateRequest(req *WaiterRequest) { 110 w.requestChannel <- req 111 //ffchan.NewTimeoutSenderShort(w.requestChannel, req, "waiterrequest") 112 } 113 114 func (w *Waiter) UpdateContext(context WaiterContext) { 115 w.contextChannel <- context 116 //ffchan.NewTimeoutSenderShort(w.contextChannel, context, "waitercontext") 117 }