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  }