Merge bitcoin/bitcoin#30111: locks: introduce mutex for tx download, flush rejection filters once per tip change

c85accecaf [refactor] delete EraseTxNoLock, just use EraseTx (glozow)
6ff84069a5 remove obsoleted TxOrphanage::m_mutex (glozow)
61745c7451 lock m_recent_confirmed_transactions using m_tx_download_mutex (glozow)
723ea0f9a5 remove obsoleted hashRecentRejectsChainTip (glozow)
18a4355250 update recent_rejects filters on ActiveTipChange (glozow)
36f170d879 add ValidationInterface::ActiveTipChange (glozow)
3eb1307df0 guard TxRequest and rejection caches with new mutex (glozow)

Pull request description:

  See #27463 for full project tracking.

  This contains the first few commits of #30110, which require some thinking about thread safety in review.
  - Introduce a new `m_tx_download_mutex` which guards the transaction download data structures including `m_txrequest`, the rolling bloom filters, and `m_orphanage`. Later this should become the mutex guarding `TxDownloadManager`.
    - `m_txrequest` doesn't need to be guarded using `cs_main` anymore
    - `m_recent_confirmed_transactions` doesn't need its own lock anymore
    - `m_orphanage` doesn't need its own lock anymore
  - Adds a new `ValidationInterface` event, `ActiveTipChanged`, which is a synchronous callback whenever the tip of the active chainstate changes.
  - Flush `m_recent_rejects` and `m_recent_rejects_reconsiderable` on `ActiveTipChanged` just once instead of checking the tip every time `AlreadyHaveTx` is called. This should speed up calls to that function (no longer comparing a block hash each time) and removes the need to lock `cs_main` every time it is called.

  Motivation:
  - These data structures need synchronization. While we are holding `m_tx_download_mutex`, these should hold:
    - a tx hash in `m_txrequest` is not also in `m_orphanage`
    - a tx hash in `m_txrequest` is not also in `m_recent_rejects` or `m_recent_confirmed_transactions`
    - In the future, orphan resolution tracking should also be synchronized. If a tx has an entry in the orphan resolution tracker, it is also in `m_orphanage`, and not in `m_txrequest`, etc.
  - Currently, `cs_main` is used to e.g. sync accesses to `m_txrequest`. We should not broaden the scope of things it locks.
  - Currently, we need to know the current chainstate every time we call `AlreadyHaveTx` so we can decide whether we should update it. Every call compares the current tip hash with `hashRecentRejectsChainTip`. It is more efficient to have a validation interface callback that updates the rejection filters whenever the chain tip changes.

ACKs for top commit:
  instagibbs:
    reACK c85accecaf
  dergoegge:
    Code review ACK c85accecaf
  theStack:
    Light code-review ACK c85accecaf
  hebasto:
    ACK c85accecaf, I have reviewed the code and it looks OK.

Tree-SHA512: c3bd524b5de1cafc9a10770dadb484cc479d6d4c687d80dd0f176d339fd95f73b85cb44cb3b6b464d38a52e20feda00aa2a1da5a73339e31831687e4bd0aa0c5
This commit is contained in:
merge-script
2024-07-24 09:30:28 +01:00
7 changed files with 128 additions and 124 deletions

View File

@@ -3473,6 +3473,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
{
LOCK(cs_main);
{
// Lock transaction pool for at least as long as it takes for connectTrace to be consumed
LOCK(MempoolMutex());
const bool was_in_ibd = m_chainman.IsInitialBlockDownload();
@@ -3549,7 +3550,12 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
break;
}
}
}
} // release MempoolMutex
// Notify external listeners about the new tip, even if pindexFork == pindexNewTip.
if (m_chainman.m_options.signals && this == &m_chainman.ActiveChainstate()) {
m_chainman.m_options.signals->ActiveTipChange(pindexNewTip, m_chainman.IsInitialBlockDownload());
}
} // release cs_main
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
if (exited_ibd) {
@@ -3768,6 +3774,12 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
// distinguish user-initiated invalidateblock changes from other
// changes.
(void)m_chainman.GetNotifications().blockTip(GetSynchronizationState(m_chainman.IsInitialBlockDownload(), m_chainman.m_blockman.m_blockfiles_indexed), *to_mark_failed->pprev);
// Fire ActiveTipChange now for the current chain tip to make sure clients are notified.
// ActivateBestChain may call this as well, but not necessarily.
if (m_chainman.m_options.signals) {
m_chainman.m_options.signals->ActiveTipChange(m_chain.Tip(), m_chainman.IsInitialBlockDownload());
}
}
return true;
}