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  }