github.com/onflow/flow-go@v0.33.17/consensus/hotstuff/tracker/tracker.go (about) 1 package tracker 2 3 import ( 4 "unsafe" 5 6 "go.uber.org/atomic" 7 8 "github.com/onflow/flow-go/consensus/hotstuff" 9 "github.com/onflow/flow-go/consensus/hotstuff/model" 10 "github.com/onflow/flow-go/model/flow" 11 ) 12 13 // NewestQCTracker is a helper structure which keeps track of the newest QC(by view) 14 // in concurrency safe way. 15 type NewestQCTracker struct { 16 newestQC *atomic.UnsafePointer 17 } 18 19 func NewNewestQCTracker() *NewestQCTracker { 20 tracker := &NewestQCTracker{ 21 newestQC: atomic.NewUnsafePointer(unsafe.Pointer(nil)), 22 } 23 return tracker 24 } 25 26 // Track updates local state of NewestQC if the provided instance is newer(by view) 27 // Concurrently safe 28 func (t *NewestQCTracker) Track(qc *flow.QuorumCertificate) bool { 29 // to record the newest value that we have ever seen we need to use loop 30 // with CAS atomic operation to make sure that we always write the latest value 31 // in case of shared access to updated value. 32 for { 33 // take a snapshot 34 newestQC := t.NewestQC() 35 // verify that our update makes sense 36 if newestQC != nil && newestQC.View >= qc.View { 37 return false 38 } 39 // attempt to install new value, repeat in case of shared update. 40 if t.newestQC.CompareAndSwap(unsafe.Pointer(newestQC), unsafe.Pointer(qc)) { 41 return true 42 } 43 } 44 } 45 46 // NewestQC returns the newest QC(by view) tracked. 47 // Concurrently safe. 48 func (t *NewestQCTracker) NewestQC() *flow.QuorumCertificate { 49 return (*flow.QuorumCertificate)(t.newestQC.Load()) 50 } 51 52 // NewestTCTracker is a helper structure which keeps track of the newest TC(by view) 53 // in concurrency safe way. 54 type NewestTCTracker struct { 55 newestTC *atomic.UnsafePointer 56 } 57 58 func NewNewestTCTracker() *NewestTCTracker { 59 tracker := &NewestTCTracker{ 60 newestTC: atomic.NewUnsafePointer(unsafe.Pointer(nil)), 61 } 62 return tracker 63 } 64 65 // Track updates local state of NewestTC if the provided instance is newer(by view) 66 // Concurrently safe. 67 func (t *NewestTCTracker) Track(tc *flow.TimeoutCertificate) bool { 68 // to record the newest value that we have ever seen we need to use loop 69 // with CAS atomic operation to make sure that we always write the latest value 70 // in case of shared access to updated value. 71 for { 72 // take a snapshot 73 newestTC := t.NewestTC() 74 // verify that our update makes sense 75 if newestTC != nil && newestTC.View >= tc.View { 76 return false 77 } 78 // attempt to install new value, repeat in case of shared update. 79 if t.newestTC.CompareAndSwap(unsafe.Pointer(newestTC), unsafe.Pointer(tc)) { 80 return true 81 } 82 } 83 } 84 85 // NewestTC returns the newest TC(by view) tracked. 86 // Concurrently safe. 87 func (t *NewestTCTracker) NewestTC() *flow.TimeoutCertificate { 88 return (*flow.TimeoutCertificate)(t.newestTC.Load()) 89 } 90 91 // NewestBlockTracker is a helper structure which keeps track of the newest block (by view) 92 // in concurrency safe way. 93 type NewestBlockTracker struct { 94 newestBlock *atomic.UnsafePointer 95 } 96 97 func NewNewestBlockTracker() *NewestBlockTracker { 98 tracker := &NewestBlockTracker{ 99 newestBlock: atomic.NewUnsafePointer(unsafe.Pointer(nil)), 100 } 101 return tracker 102 } 103 104 // Track updates local state of newestBlock if the provided instance is newer (by view) 105 // Concurrently safe. 106 func (t *NewestBlockTracker) Track(block *model.Block) bool { 107 // to record the newest value that we have ever seen we need to use loop 108 // with CAS atomic operation to make sure that we always write the latest value 109 // in case of shared access to updated value. 110 for { 111 // take a snapshot 112 newestBlock := t.NewestBlock() 113 // verify that our update makes sense 114 if newestBlock != nil && newestBlock.View >= block.View { 115 return false 116 } 117 // attempt to install new value, repeat in case of shared update. 118 if t.newestBlock.CompareAndSwap(unsafe.Pointer(newestBlock), unsafe.Pointer(block)) { 119 return true 120 } 121 } 122 } 123 124 // NewestBlock returns the newest block (by view) tracked. 125 // Concurrently safe. 126 func (t *NewestBlockTracker) NewestBlock() *model.Block { 127 return (*model.Block)(t.newestBlock.Load()) 128 } 129 130 // NewestPartialTcTracker tracks the newest partial TC (by view) in a concurrency safe way. 131 type NewestPartialTcTracker struct { 132 newestPartialTc *atomic.UnsafePointer 133 } 134 135 func NewNewestPartialTcTracker() *NewestPartialTcTracker { 136 tracker := &NewestPartialTcTracker{ 137 newestPartialTc: atomic.NewUnsafePointer(unsafe.Pointer(nil)), 138 } 139 return tracker 140 } 141 142 // Track updates local state of newestPartialTc if the provided instance is newer (by view) 143 // Concurrently safe. 144 func (t *NewestPartialTcTracker) Track(partialTc *hotstuff.PartialTcCreated) bool { 145 // To record the newest value that we have ever seen, we need to use loop 146 // with CAS atomic operation to make sure that we always write the latest value 147 // in case of shared access to updated value. 148 for { 149 // take a snapshot 150 newestPartialTc := t.NewestPartialTc() 151 // verify that our partial TC is from a newer view 152 if newestPartialTc != nil && newestPartialTc.View >= partialTc.View { 153 return false 154 } 155 // attempt to install new value, repeat in case of shared update. 156 if t.newestPartialTc.CompareAndSwap(unsafe.Pointer(newestPartialTc), unsafe.Pointer(partialTc)) { 157 return true 158 } 159 } 160 } 161 162 // NewestPartialTc returns the newest partial TC (by view) tracked. 163 // Concurrently safe. 164 func (t *NewestPartialTcTracker) NewestPartialTc() *hotstuff.PartialTcCreated { 165 return (*hotstuff.PartialTcCreated)(t.newestPartialTc.Load()) 166 }