|
21 | 21 | #include <primitives/transaction.h>
|
22 | 22 | #include <random.h>
|
23 | 23 | #include <sync.h>
|
| 24 | +#include <util/epochguard.h> |
24 | 25 | #include <util/hasher.h>
|
25 | 26 |
|
26 | 27 | #include <boost/multi_index_container.hpp>
|
@@ -63,6 +64,7 @@ struct CompareIteratorByHash {
|
63 | 64 | return a->GetTx().GetHash() < b->GetTx().GetHash();
|
64 | 65 | }
|
65 | 66 | };
|
| 67 | + |
66 | 68 | /** \class CTxMemPoolEntry
|
67 | 69 | *
|
68 | 70 | * CTxMemPoolEntry stores data about the corresponding transaction, as well
|
@@ -155,7 +157,7 @@ class CTxMemPoolEntry
|
155 | 157 | Children& GetMemPoolChildren() const { return m_children; }
|
156 | 158 |
|
157 | 159 | mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes
|
158 |
| - mutable uint64_t m_epoch; //!< epoch when last touched, useful for graph algorithms |
| 160 | + mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms |
159 | 161 | };
|
160 | 162 |
|
161 | 163 | // Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
|
@@ -485,8 +487,7 @@ class CTxMemPool
|
485 | 487 | mutable int64_t lastRollingFeeUpdate;
|
486 | 488 | mutable bool blockSinceLastRollingFeeBump;
|
487 | 489 | mutable double rollingMinimumFeeRate; //!< minimum fee to get into the pool, decreases exponentially
|
488 |
| - mutable uint64_t m_epoch{0}; |
489 |
| - mutable bool m_has_epoch_guard{false}; |
| 490 | + mutable Epoch m_epoch GUARDED_BY(cs); |
490 | 491 |
|
491 | 492 | // In-memory counter for external mempool tracking purposes.
|
492 | 493 | // This number is incremented once every time a transaction
|
@@ -665,7 +666,7 @@ class CTxMemPool
|
665 | 666 | * for). Note: vHashesToUpdate should be the set of transactions from the
|
666 | 667 | * disconnected block that have been accepted back into the mempool.
|
667 | 668 | */
|
668 |
| - void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main); |
| 669 | + void UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main) LOCKS_EXCLUDED(m_epoch); |
669 | 670 |
|
670 | 671 | /** Try to calculate all in-mempool ancestors of entry.
|
671 | 672 | * (these are all calculated including the tx itself)
|
@@ -826,52 +827,22 @@ class CTxMemPool
|
826 | 827 | */
|
827 | 828 | void removeUnchecked(txiter entry, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
828 | 829 | public:
|
829 |
| - /** EpochGuard: RAII-style guard for using epoch-based graph traversal algorithms. |
830 |
| - * When walking ancestors or descendants, we generally want to avoid |
831 |
| - * visiting the same transactions twice. Some traversal algorithms use |
832 |
| - * std::set (or setEntries) to deduplicate the transaction we visit. |
833 |
| - * However, use of std::set is algorithmically undesirable because it both |
834 |
| - * adds an asymptotic factor of O(log n) to traverals cost and triggers O(n) |
835 |
| - * more dynamic memory allocations. |
836 |
| - * In many algorithms we can replace std::set with an internal mempool |
837 |
| - * counter to track the time (or, "epoch") that we began a traversal, and |
838 |
| - * check + update a per-transaction epoch for each transaction we look at to |
839 |
| - * determine if that transaction has not yet been visited during the current |
840 |
| - * traversal's epoch. |
841 |
| - * Algorithms using std::set can be replaced on a one by one basis. |
842 |
| - * Both techniques are not fundamentally incompatible across the codebase. |
843 |
| - * Generally speaking, however, the remaining use of std::set for mempool |
844 |
| - * traversal should be viewed as a TODO for replacement with an epoch based |
845 |
| - * traversal, rather than a preference for std::set over epochs in that |
846 |
| - * algorithm. |
847 |
| - */ |
848 |
| - class EpochGuard { |
849 |
| - const CTxMemPool& pool; |
850 |
| - public: |
851 |
| - explicit EpochGuard(const CTxMemPool& in); |
852 |
| - ~EpochGuard(); |
853 |
| - }; |
854 |
| - // N.B. GetFreshEpoch modifies mutable state via the EpochGuard construction |
855 |
| - // (and later destruction) |
856 |
| - EpochGuard GetFreshEpoch() const EXCLUSIVE_LOCKS_REQUIRED(cs); |
857 |
| - |
858 | 830 | /** visited marks a CTxMemPoolEntry as having been traversed
|
859 |
| - * during the lifetime of the most recently created EpochGuard |
| 831 | + * during the lifetime of the most recently created Epoch::Guard |
860 | 832 | * and returns false if we are the first visitor, true otherwise.
|
861 | 833 | *
|
862 |
| - * An EpochGuard must be held when visited is called or an assert will be |
| 834 | + * An Epoch::Guard must be held when visited is called or an assert will be |
863 | 835 | * triggered.
|
864 | 836 | *
|
865 | 837 | */
|
866 |
| - bool visited(txiter it) const EXCLUSIVE_LOCKS_REQUIRED(cs) { |
867 |
| - assert(m_has_epoch_guard); |
868 |
| - bool ret = it->m_epoch >= m_epoch; |
869 |
| - it->m_epoch = std::max(it->m_epoch, m_epoch); |
870 |
| - return ret; |
| 838 | + bool visited(const txiter it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch) |
| 839 | + { |
| 840 | + return m_epoch.visited(it->m_epoch_marker); |
871 | 841 | }
|
872 | 842 |
|
873 |
| - bool visited(Optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs) { |
874 |
| - assert(m_has_epoch_guard); |
| 843 | + bool visited(Optional<txiter> it) const EXCLUSIVE_LOCKS_REQUIRED(cs, m_epoch) |
| 844 | + { |
| 845 | + assert(m_epoch.guarded()); // verify guard even when it==nullopt |
875 | 846 | return !it || visited(*it);
|
876 | 847 | }
|
877 | 848 | };
|
|
0 commit comments