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 }