core/txpool/legacypool: protect cache with mutex (#27898)
This change fixes the a potential race by using mutexes when the m.cache is read or modified.
This commit is contained in:
parent
32fde3f838
commit
1aa5520d75
@ -53,9 +53,10 @@ func (h *nonceHeap) Pop() interface{} {
|
|||||||
// sortedMap is a nonce->transaction hash map with a heap based index to allow
|
// sortedMap is a nonce->transaction hash map with a heap based index to allow
|
||||||
// iterating over the contents in a nonce-incrementing way.
|
// iterating over the contents in a nonce-incrementing way.
|
||||||
type sortedMap struct {
|
type sortedMap struct {
|
||||||
items map[uint64]*types.Transaction // Hash map storing the transaction data
|
items map[uint64]*types.Transaction // Hash map storing the transaction data
|
||||||
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
|
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
|
||||||
cache types.Transactions // Cache of the transactions already sorted
|
cache types.Transactions // Cache of the transactions already sorted
|
||||||
|
cacheMu sync.Mutex // Mutex covering the cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSortedMap creates a new nonce-sorted transaction map.
|
// newSortedMap creates a new nonce-sorted transaction map.
|
||||||
@ -78,7 +79,9 @@ func (m *sortedMap) Put(tx *types.Transaction) {
|
|||||||
if m.items[nonce] == nil {
|
if m.items[nonce] == nil {
|
||||||
heap.Push(m.index, nonce)
|
heap.Push(m.index, nonce)
|
||||||
}
|
}
|
||||||
|
m.cacheMu.Lock()
|
||||||
m.items[nonce], m.cache = tx, nil
|
m.items[nonce], m.cache = tx, nil
|
||||||
|
m.cacheMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward removes all transactions from the map with a nonce lower than the
|
// Forward removes all transactions from the map with a nonce lower than the
|
||||||
@ -94,9 +97,11 @@ func (m *sortedMap) Forward(threshold uint64) types.Transactions {
|
|||||||
delete(m.items, nonce)
|
delete(m.items, nonce)
|
||||||
}
|
}
|
||||||
// If we had a cached order, shift the front
|
// If we had a cached order, shift the front
|
||||||
|
m.cacheMu.Lock()
|
||||||
if m.cache != nil {
|
if m.cache != nil {
|
||||||
m.cache = m.cache[len(removed):]
|
m.cache = m.cache[len(removed):]
|
||||||
}
|
}
|
||||||
|
m.cacheMu.Unlock()
|
||||||
return removed
|
return removed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +125,9 @@ func (m *sortedMap) reheap() {
|
|||||||
*m.index = append(*m.index, nonce)
|
*m.index = append(*m.index, nonce)
|
||||||
}
|
}
|
||||||
heap.Init(m.index)
|
heap.Init(m.index)
|
||||||
|
m.cacheMu.Lock()
|
||||||
m.cache = nil
|
m.cache = nil
|
||||||
|
m.cacheMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter is identical to Filter, but **does not** regenerate the heap. This method
|
// filter is identical to Filter, but **does not** regenerate the heap. This method
|
||||||
@ -136,7 +143,9 @@ func (m *sortedMap) filter(filter func(*types.Transaction) bool) types.Transacti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(removed) > 0 {
|
if len(removed) > 0 {
|
||||||
|
m.cacheMu.Lock()
|
||||||
m.cache = nil
|
m.cache = nil
|
||||||
|
m.cacheMu.Unlock()
|
||||||
}
|
}
|
||||||
return removed
|
return removed
|
||||||
}
|
}
|
||||||
@ -160,9 +169,11 @@ func (m *sortedMap) Cap(threshold int) types.Transactions {
|
|||||||
heap.Init(m.index)
|
heap.Init(m.index)
|
||||||
|
|
||||||
// If we had a cache, shift the back
|
// If we had a cache, shift the back
|
||||||
|
m.cacheMu.Lock()
|
||||||
if m.cache != nil {
|
if m.cache != nil {
|
||||||
m.cache = m.cache[:len(m.cache)-len(drops)]
|
m.cache = m.cache[:len(m.cache)-len(drops)]
|
||||||
}
|
}
|
||||||
|
m.cacheMu.Unlock()
|
||||||
return drops
|
return drops
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +193,9 @@ func (m *sortedMap) Remove(nonce uint64) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(m.items, nonce)
|
delete(m.items, nonce)
|
||||||
|
m.cacheMu.Lock()
|
||||||
m.cache = nil
|
m.cache = nil
|
||||||
|
m.cacheMu.Unlock()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -206,7 +219,9 @@ func (m *sortedMap) Ready(start uint64) types.Transactions {
|
|||||||
delete(m.items, next)
|
delete(m.items, next)
|
||||||
heap.Pop(m.index)
|
heap.Pop(m.index)
|
||||||
}
|
}
|
||||||
|
m.cacheMu.Lock()
|
||||||
m.cache = nil
|
m.cache = nil
|
||||||
|
m.cacheMu.Unlock()
|
||||||
|
|
||||||
return ready
|
return ready
|
||||||
}
|
}
|
||||||
@ -217,6 +232,8 @@ func (m *sortedMap) Len() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *sortedMap) flatten() types.Transactions {
|
func (m *sortedMap) flatten() types.Transactions {
|
||||||
|
m.cacheMu.Lock()
|
||||||
|
defer m.cacheMu.Unlock()
|
||||||
// If the sorting was not cached yet, create and cache it
|
// If the sorting was not cached yet, create and cache it
|
||||||
if m.cache == nil {
|
if m.cache == nil {
|
||||||
m.cache = make(types.Transactions, 0, len(m.items))
|
m.cache = make(types.Transactions, 0, len(m.items))
|
||||||
@ -232,8 +249,8 @@ func (m *sortedMap) flatten() types.Transactions {
|
|||||||
// sorted internal representation. The result of the sorting is cached in case
|
// sorted internal representation. The result of the sorting is cached in case
|
||||||
// it's requested again before any modifications are made to the contents.
|
// it's requested again before any modifications are made to the contents.
|
||||||
func (m *sortedMap) Flatten() types.Transactions {
|
func (m *sortedMap) Flatten() types.Transactions {
|
||||||
// Copy the cache to prevent accidental modifications
|
|
||||||
cache := m.flatten()
|
cache := m.flatten()
|
||||||
|
// Copy the cache to prevent accidental modification
|
||||||
txs := make(types.Transactions, len(cache))
|
txs := make(types.Transactions, len(cache))
|
||||||
copy(txs, cache)
|
copy(txs, cache)
|
||||||
return txs
|
return txs
|
||||||
|
Loading…
Reference in New Issue
Block a user