Branch data Line data Source code
1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto 2 : : // Copyright (c) 2009-2022 The Bitcoin Core developers 3 : : // Distributed under the MIT software license, see the accompanying 4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 : : 6 : : #ifndef BITCOIN_UTIL_EPOCHGUARD_H 7 : : #define BITCOIN_UTIL_EPOCHGUARD_H 8 : : 9 : : #include <threadsafety.h> 10 : : #include <util/macros.h> 11 : : 12 : : #include <cassert> 13 : : 14 : : /** Epoch: RAII-style guard for using epoch-based graph traversal algorithms. 15 : : * When walking ancestors or descendants, we generally want to avoid 16 : : * visiting the same transactions twice. Some traversal algorithms use 17 : : * std::set (or setEntries) to deduplicate the transaction we visit. 18 : : * However, use of std::set is algorithmically undesirable because it both 19 : : * adds an asymptotic factor of O(log n) to traversals cost and triggers O(n) 20 : : * more dynamic memory allocations. 21 : : * In many algorithms we can replace std::set with an internal mempool 22 : : * counter to track the time (or, "epoch") that we began a traversal, and 23 : : * check + update a per-transaction epoch for each transaction we look at to 24 : : * determine if that transaction has not yet been visited during the current 25 : : * traversal's epoch. 26 : : * Algorithms using std::set can be replaced on a one by one basis. 27 : : * Both techniques are not fundamentally incompatible across the codebase. 28 : : * Generally speaking, however, the remaining use of std::set for mempool 29 : : * traversal should be viewed as a TODO for replacement with an epoch based 30 : : * traversal, rather than a preference for std::set over epochs in that 31 : : * algorithm. 32 : : */ 33 : : 34 : : class LOCKABLE Epoch 35 : : { 36 : : private: 37 : 1 : uint64_t m_raw_epoch = 0; 38 : 1 : bool m_guarded = false; 39 : : 40 : : public: 41 : 2 : Epoch() = default; 42 : : Epoch(const Epoch&) = delete; 43 : : Epoch& operator=(const Epoch&) = delete; 44 : : Epoch(Epoch&&) = delete; 45 : : Epoch& operator=(Epoch&&) = delete; 46 : : ~Epoch() = default; 47 : : 48 : : bool guarded() const { return m_guarded; } 49 : : 50 : : class Marker 51 : : { 52 : : private: 53 : 0 : uint64_t m_marker = 0; 54 : : 55 : : // only allow modification via Epoch member functions 56 : : friend class Epoch; 57 : : Marker& operator=(const Marker&) = delete; 58 : : 59 : : public: 60 : 0 : Marker() = default; 61 : : Marker(const Marker&) = default; 62 : : Marker(Marker&&) = delete; 63 : : Marker& operator=(Marker&&) = delete; 64 : : ~Marker() = default; 65 : : }; 66 : : 67 : : class SCOPED_LOCKABLE Guard 68 : : { 69 : : private: 70 : : Epoch& m_epoch; 71 : : 72 : : public: 73 : 0 : explicit Guard(Epoch& epoch) EXCLUSIVE_LOCK_FUNCTION(epoch) : m_epoch(epoch) 74 : : { 75 [ # # ]: 0 : assert(!m_epoch.m_guarded); 76 : 0 : ++m_epoch.m_raw_epoch; 77 : 0 : m_epoch.m_guarded = true; 78 : 0 : } 79 : 0 : ~Guard() UNLOCK_FUNCTION() 80 : : { 81 [ # # ]: 0 : assert(m_epoch.m_guarded); 82 : 0 : ++m_epoch.m_raw_epoch; // ensure clear separation between epochs 83 : 0 : m_epoch.m_guarded = false; 84 : 0 : } 85 : : }; 86 : : 87 : 0 : bool visited(Marker& marker) const EXCLUSIVE_LOCKS_REQUIRED(*this) 88 : : { 89 [ # # ]: 0 : assert(m_guarded); 90 [ # # ]: 0 : if (marker.m_marker < m_raw_epoch) { 91 : : // marker is from a previous epoch, so this is its first visit 92 : 0 : marker.m_marker = m_raw_epoch; 93 : 0 : return false; 94 : : } else { 95 : 0 : return true; 96 : : } 97 : 0 : } 98 : : }; 99 : : 100 : : #define WITH_FRESH_EPOCH(epoch) const Epoch::Guard UNIQUE_NAME(epoch_guard_)(epoch) 101 : : 102 : : #endif // BITCOIN_UTIL_EPOCHGUARD_H