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 }