gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/miningpool/transactionlist.go (about)

     1  package pool
     2  
     3  import (
     4  	"sync"
     5  
     6  	"gitlab.com/SiaPrime/SiaPrime/build"
     7  	"gitlab.com/SiaPrime/SiaPrime/types"
     8  )
     9  
    10  type (
    11  	// txnList is a helper structure to allow for quicker lookups, inserts and
    12  	// removals of transactions that should go into a block.
    13  	// In addition to the unsolved block's transaction field, the miner also
    14  	// keeps a txnList in memory. The txnList contains the transactions that
    15  	// will be in the unsolved block the next time blockForWork is called.
    16  	// Whenever we move a transaction from the overflow heap into the block
    17  	// heap, we also append it to the txnList. If we remove a transaction from
    18  	// the block heap we also remove it from the txnList. By using a map to map
    19  	// a transaction id to its element in the list, we can search, insert and
    20  	// remove in constant time. Removing an element from the txnList doesn't
    21  	// change the order of the remaining elements in the list. This is
    22  	// important since we don't want to mess up the relative order of
    23  	// transactions in their transaction sets.
    24  	// Every time blockForWork is called, we copy the transactions from the
    25  	// txnList into a slice that is then assigned to be the unsolved block's
    26  	// updated transaction field. To avoid allocating new memory every time
    27  	// this happens, a preallocated slice is kept in memory. Memory allocation
    28  	// for txnListElements is also optimized by using a memory pool that
    29  	// recycles txnListElements during rapid deletion and insertion.
    30  	txnList struct {
    31  		first            *txnListElement                         // pointer to first element in list
    32  		last             *txnListElement                         // pointer to last element in list
    33  		idToTxn          map[types.TransactionID]*txnListElement // maps transaction ids to list element
    34  		preallocatedTxns []types.Transaction                     // used to return the list's contents without having to reallocate
    35  	}
    36  	// txnListElemenet is a single element in a txnList
    37  	txnListElement struct {
    38  		txn  *types.Transaction
    39  		prev *txnListElement
    40  		next *txnListElement
    41  	}
    42  )
    43  
    44  // A pool to reduce the amount of memory allocations when elements are removed
    45  // and inserted rapidly.
    46  var listElementPool = sync.Pool{
    47  	New: func() interface{} {
    48  		return &txnListElement{}
    49  	},
    50  }
    51  
    52  // newTxnList creates a new instance of the txnList
    53  func newTxnList() *txnList {
    54  	return &txnList{
    55  		idToTxn: make(map[types.TransactionID]*txnListElement),
    56  	}
    57  }
    58  
    59  // newListElement returns a list element with all fields initialized to their
    60  // default values
    61  func newListElement() *txnListElement {
    62  	listElement := listElementPool.Get().(*txnListElement)
    63  	listElement.prev = nil
    64  	listElement.next = nil
    65  	return listElement
    66  }
    67  
    68  // appendTxn appends a transaction to the list
    69  func (tl *txnList) appendTxn(txn *types.Transaction) {
    70  	// Create the element and store it in idToTxn for easier lookup by id
    71  	listElement := newListElement()
    72  	listElement.txn = txn
    73  	tl.idToTxn[txn.ID()] = listElement
    74  
    75  	// check if it is the first element
    76  	if tl.first == nil {
    77  		tl.first = listElement
    78  		tl.last = listElement
    79  		return
    80  	}
    81  	// if not append it
    82  	tl.last.next = listElement
    83  	listElement.prev = tl.last
    84  	tl.last = listElement
    85  }
    86  
    87  // transactions returns the transactions contained in the list as a slice
    88  func (tl *txnList) transactions() []types.Transaction {
    89  	if tl.first == nil {
    90  		return []types.Transaction{}
    91  	}
    92  	element := tl.first
    93  	tl.preallocatedTxns = tl.preallocatedTxns[:0]
    94  	for element != nil {
    95  		tl.preallocatedTxns = append(tl.preallocatedTxns, *element.txn)
    96  		element = element.next
    97  	}
    98  	return tl.preallocatedTxns
    99  }
   100  
   101  // removeTxn removes a transaction by id
   102  func (tl *txnList) removeTxn(id types.TransactionID) {
   103  	// Get the corresponding list element and remove it from the map
   104  	listElement, exists := tl.idToTxn[id]
   105  	if !exists {
   106  		build.Critical("transaction is not in the list")
   107  		return
   108  	}
   109  	delete(tl.idToTxn, id)
   110  	defer listElementPool.Put(listElement)
   111  
   112  	pe := listElement.prev
   113  	ne := listElement.next
   114  	if pe == nil {
   115  		// listElement is the first element. Set the following element to be
   116  		// the next first element of the list
   117  		tl.first = listElement.next
   118  		// If the new first element is not nil its prev field should be set to nil
   119  		if tl.first != nil {
   120  			tl.first.prev = nil
   121  		}
   122  	}
   123  	if ne == nil {
   124  		// listElement is the last element. Set the previous element to be the
   125  		// next last element of the list and its next field should be nil
   126  		tl.last = listElement.prev
   127  		// If the new last element is not nil its next field should be set to nil
   128  		if tl.last != nil {
   129  			tl.last.next = nil
   130  		}
   131  	}
   132  	// Link pe and ne to each other if they both exist
   133  	if pe != nil && ne != nil {
   134  		pe.next = ne
   135  		ne.prev = pe
   136  	}
   137  }