github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/events/gadgets/views.go (about)

     1  package gadgets
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/onflow/flow-go/model/flow"
     7  	"github.com/onflow/flow-go/state/protocol/events"
     8  )
     9  
    10  // Views is a protocol events consumer that provides an interface to subscribe
    11  // to callbacks when consensus reaches a particular view. When a callback is
    12  // registered for a view, it will be invoked when the first block with
    13  // block.View >= V is finalized. Callbacks for earlier views are executed before
    14  // callbacks for later views, and callbacks for the same view are executed on a
    15  // FIFO basis.
    16  type Views struct {
    17  	sync.Mutex
    18  	events.Noop
    19  	callbacks    map[uint64][]events.OnViewCallback
    20  	orderedViews []uint64
    21  }
    22  
    23  // NewViews returns a new Views events gadget.
    24  func NewViews() *Views {
    25  	views := &Views{
    26  		callbacks:    make(map[uint64][]events.OnViewCallback),
    27  		orderedViews: make([]uint64, 0),
    28  	}
    29  	return views
    30  }
    31  
    32  // OnView registers the callback for the given view.
    33  func (v *Views) OnView(view uint64, callback events.OnViewCallback) {
    34  	v.Lock()
    35  	defer v.Unlock()
    36  
    37  	// index a view the first time we see it
    38  	callbacks := v.callbacks[view]
    39  	if len(callbacks) == 0 {
    40  		v.indexView(view)
    41  	}
    42  	v.callbacks[view] = append(callbacks, callback)
    43  }
    44  
    45  func (v *Views) indexView(view uint64) {
    46  	// no indexed views
    47  	if len(v.orderedViews) == 0 {
    48  		v.orderedViews = append(v.orderedViews, view)
    49  		return
    50  	}
    51  
    52  	// find the insertion index in the ordered list of views
    53  	// start with higher views to match typical usage patterns
    54  	insertAt := 0
    55  	for i := len(v.orderedViews) - 1; i >= 0; i-- {
    56  		viewI := v.orderedViews[i]
    57  		if view > viewI {
    58  			insertAt = i + 1
    59  			break
    60  		}
    61  	}
    62  	// shift the list right, insert the new view
    63  	v.orderedViews = append(v.orderedViews, 0)                   // add capacity (will be overwritten)
    64  	copy(v.orderedViews[insertAt+1:], v.orderedViews[insertAt:]) // shift larger views right
    65  	v.orderedViews[insertAt] = view                              // insert new view
    66  }
    67  
    68  // BlockFinalized handles block finalized protocol events, triggering view
    69  // callbacks as needed.
    70  func (v *Views) BlockFinalized(block *flow.Header) {
    71  	v.Lock()
    72  	defer v.Unlock()
    73  
    74  	blockView := block.View
    75  
    76  	// the index (inclusive) of the lowest view which should be kept
    77  	cutoff := 0
    78  	for i, view := range v.orderedViews {
    79  		if view > blockView {
    80  			break
    81  		}
    82  		for _, callback := range v.callbacks[view] {
    83  			callback(block)
    84  		}
    85  		delete(v.callbacks, view)
    86  		cutoff = i + 1
    87  	}
    88  
    89  	// we have no other queued view callbacks
    90  	if cutoff >= len(v.orderedViews) {
    91  		if len(v.orderedViews) > 0 {
    92  			v.orderedViews = []uint64{}
    93  		}
    94  		return
    95  	}
    96  	// remove view callbacks which have been invoked
    97  	v.orderedViews = v.orderedViews[cutoff:]
    98  }