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 : : uint64_t m_raw_epoch = 0;
38 : : bool m_guarded = false;
39 : :
40 : : public:
41 : 180 : 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 : : 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 : 27720 : 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 : 53 : explicit Guard(Epoch& epoch) EXCLUSIVE_LOCK_FUNCTION(epoch) : m_epoch(epoch)
74 : : {
75 [ - + ]: 53 : assert(!m_epoch.m_guarded);
76 : 53 : ++m_epoch.m_raw_epoch;
77 : 53 : m_epoch.m_guarded = true;
78 : 53 : }
79 : 53 : ~Guard() UNLOCK_FUNCTION()
80 : : {
81 [ - + ]: 53 : assert(m_epoch.m_guarded);
82 : 53 : ++m_epoch.m_raw_epoch; // ensure clear separation between epochs
83 : 53 : m_epoch.m_guarded = false;
84 : 53 : }
85 : : };
86 : :
87 : 3391 : bool visited(Marker& marker) const EXCLUSIVE_LOCKS_REQUIRED(*this)
88 : : {
89 [ - + ]: 3391 : assert(m_guarded);
90 [ + + ]: 3391 : if (marker.m_marker < m_raw_epoch) {
91 : : // marker is from a previous epoch, so this is its first visit
92 : 1741 : marker.m_marker = m_raw_epoch;
93 : 1741 : return false;
94 : : } else {
95 : : return true;
96 : : }
97 : : }
98 : : };
99 : :
100 : : #define WITH_FRESH_EPOCH(epoch) const Epoch::Guard UNIQUE_NAME(epoch_guard_)(epoch)
101 : :
102 : : #endif // BITCOIN_UTIL_EPOCHGUARD_H
|