github.com/annchain/OG@v0.0.9/poc/tendermint/waiter.go (about)

     1  package tendermint
     2  
     3  import (
     4  	"github.com/annchain/OG/ffchan"
     5  	"github.com/sirupsen/logrus"
     6  	"time"
     7  )
     8  
     9  type WaiterContext interface {
    10  	Equal(WaiterContext) bool
    11  	IsAfter(WaiterContext) bool
    12  }
    13  
    14  type WaiterRequest struct {
    15  	WaitTime        time.Duration
    16  	Context         WaiterContext
    17  	TimeoutCallback func(WaiterContext)
    18  }
    19  
    20  // Waiter provides a way to wait for some context to be changed in a certain time.
    21  // If the context is not changed, callback function will be triggered.
    22  type Waiter struct {
    23  	currentRequest      *WaiterRequest
    24  	requestChannel      chan *WaiterRequest
    25  	contextChannel      chan WaiterContext
    26  	callbackEventChanel chan *WaiterRequest
    27  	quit                chan bool
    28  }
    29  
    30  func NewWaiter(callbackEventChannel chan *WaiterRequest) *Waiter {
    31  	return &Waiter{
    32  		requestChannel:      make(chan *WaiterRequest, 10),
    33  		contextChannel:      make(chan WaiterContext, 10),
    34  		quit:                make(chan bool),
    35  		callbackEventChanel: callbackEventChannel,
    36  	}
    37  }
    38  
    39  func (w *Waiter) StartEventLoop() {
    40  	timer := time.NewTimer(time.Duration(10))
    41  	for {
    42  		select {
    43  		case <-w.quit:
    44  			break
    45  		case request := <-w.requestChannel:
    46  			// could be an updated request
    47  			// if it is really updated request,
    48  			if w.currentRequest != nil && !request.Context.IsAfter(w.currentRequest.Context) {
    49  				// this request is before current waiting request, ignore.
    50  				continue
    51  			}
    52  			logrus.WithField("request ", request).Trace("request is newer and we will reset")
    53  			w.currentRequest = request
    54  			if !timer.Stop() {
    55  				// drain the timer but do not use the method in document
    56  				// timer may already be consumed so use a select
    57  				select {
    58  				case <-timer.C:
    59  				default:
    60  				}
    61  			}
    62  			timer.Reset(request.WaitTime)
    63  		case latestContext := <-w.contextChannel:
    64  			if w.currentRequest == nil || latestContext.IsAfter(w.currentRequest.Context) {
    65  				// a new state is updated, cancel all pending timeouts
    66  				if w.currentRequest != nil {
    67  					logrus.WithField("new", latestContext.(*TendermintContext).StepType).
    68  						WithField("old", w.currentRequest.Context.(*TendermintContext).StepType).
    69  						Debug("new state updated")
    70  				} else {
    71  					logrus.WithField("new", latestContext.(*TendermintContext).StepType).
    72  						WithField("old", nil).
    73  						Debug("new state updated")
    74  				}
    75  				if !timer.Stop() {
    76  					// drain the timer but do not use the method in document
    77  					// timer may already be consumed so use a select
    78  					select {
    79  					case <-timer.C:
    80  					default:
    81  					}
    82  				}
    83  			}
    84  		case <-timer.C:
    85  			// timeout, trigger callback
    86  			if w.currentRequest != nil {
    87  				<-ffchan.NewTimeoutSenderShort(w.callbackEventChanel, w.currentRequest, "waiterCallback").C
    88  				//w.currentRequest.TimeoutCallback(w.currentRequest.Context)
    89  			}
    90  		}
    91  	}
    92  }
    93  
    94  func (w *Waiter) UpdateRequest(req *WaiterRequest) {
    95  	w.requestChannel <- req
    96  	//ffchan.NewTimeoutSenderShort(w.requestChannel, req, "waiterrequest")
    97  }
    98  
    99  func (w *Waiter) UpdateContext(context WaiterContext) {
   100  	w.contextChannel <- context
   101  	//ffchan.NewTimeoutSenderShort(w.contextChannel, context, "waitercontext")
   102  }