Branch data Line data Source code
1 : : // Copyright (c) 2023 The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <kernel/disconnected_transactions.h>
6 : :
7 : : #include <assert.h>
8 : : #include <core_memusage.h>
9 : : #include <memusage.h>
10 : : #include <primitives/transaction.h>
11 : : #include <util/hasher.h>
12 : :
13 : : #include <memory>
14 : : #include <utility>
15 : :
16 : : // It's almost certainly a logic bug if we don't clear out queuedTx before
17 : : // destruction, as we add to it while disconnecting blocks, and then we
18 : : // need to re-process remaining transactions to ensure mempool consistency.
19 : : // For now, assert() that we've emptied out this object on destruction.
20 : : // This assert() can always be removed if the reorg-processing code were
21 : : // to be refactored such that this assumption is no longer true (for
22 : : // instance if there was some other way we cleaned up the mempool after a
23 : : // reorg, besides draining this object).
24 : 7019 : DisconnectedBlockTransactions::~DisconnectedBlockTransactions()
25 : : {
26 [ - + ]: 7019 : assert(queuedTx.empty());
27 [ - + ]: 7019 : assert(iters_by_txid.empty());
28 [ - + ]: 7019 : assert(cachedInnerUsage == 0);
29 : 7019 : }
30 : :
31 : 397 : std::vector<CTransactionRef> DisconnectedBlockTransactions::LimitMemoryUsage()
32 : : {
33 : 397 : std::vector<CTransactionRef> evicted;
34 : :
35 [ + - + - : 399 : while (!queuedTx.empty() && DynamicMemoryUsage() > m_max_mem_usage) {
+ + ]
36 [ + - ]: 2 : evicted.emplace_back(queuedTx.front());
37 : 2 : cachedInnerUsage -= RecursiveDynamicUsage(queuedTx.front());
38 [ + - ]: 2 : iters_by_txid.erase(queuedTx.front()->GetHash());
39 : 2 : queuedTx.pop_front();
40 : : }
41 : 397 : return evicted;
42 : 0 : }
43 : :
44 : 403 : size_t DisconnectedBlockTransactions::DynamicMemoryUsage() const
45 : : {
46 [ + - ]: 403 : return cachedInnerUsage + memusage::DynamicUsage(iters_by_txid) + memusage::DynamicUsage(queuedTx);
47 : : }
48 : :
49 : 397 : [[nodiscard]] std::vector<CTransactionRef> DisconnectedBlockTransactions::AddTransactionsFromBlock(const std::vector<CTransactionRef>& vtx)
50 : : {
51 : 397 : iters_by_txid.reserve(iters_by_txid.size() + vtx.size());
52 [ + + ]: 993 : for (auto block_it = vtx.rbegin(); block_it != vtx.rend(); ++block_it) {
53 : 596 : auto it = queuedTx.insert(queuedTx.end(), *block_it);
54 [ - + ]: 596 : auto [_, inserted] = iters_by_txid.emplace((*block_it)->GetHash(), it);
55 [ - + ]: 596 : assert(inserted); // callers may never pass multiple transactions with the same txid
56 : 596 : cachedInnerUsage += RecursiveDynamicUsage(*block_it);
57 : : }
58 : 397 : return LimitMemoryUsage();
59 : : }
60 : :
61 : 7384 : void DisconnectedBlockTransactions::removeForBlock(const std::vector<CTransactionRef>& vtx)
62 : : {
63 : : // Short-circuit in the common case of a block being added to the tip
64 [ + + ]: 7384 : if (queuedTx.empty()) {
65 : : return;
66 : : }
67 [ + + ]: 780 : for (const auto& tx : vtx) {
68 : 390 : auto iter = iters_by_txid.find(tx->GetHash());
69 [ - + ]: 390 : if (iter != iters_by_txid.end()) {
70 : 0 : auto list_iter = iter->second;
71 : 0 : iters_by_txid.erase(iter);
72 : 0 : cachedInnerUsage -= RecursiveDynamicUsage(*list_iter);
73 : 0 : queuedTx.erase(list_iter);
74 : : }
75 : : }
76 : : }
77 : :
78 : 20 : void DisconnectedBlockTransactions::clear()
79 : : {
80 : 20 : cachedInnerUsage = 0;
81 : 20 : iters_by_txid.clear();
82 : 20 : queuedTx.clear();
83 : 20 : }
84 : :
85 : 16 : std::list<CTransactionRef> DisconnectedBlockTransactions::take()
86 : : {
87 : 16 : std::list<CTransactionRef> ret = std::move(queuedTx);
88 [ + - ]: 16 : clear();
89 : 16 : return ret;
90 : 0 : }
|