Skip to content

perf: make m_peer_mutex a SharedMutex - #6468

Merged
PastaPastaPasta merged 1 commit into
dashpay:developfrom
PastaPastaPasta:perf-shared-mutex-m_peer_map
Oct 26, 2025
Merged

perf: make m_peer_mutex a SharedMutex#6468
PastaPastaPasta merged 1 commit into
dashpay:developfrom
PastaPastaPasta:perf-shared-mutex-m_peer_map

Conversation

@PastaPastaPasta

@PastaPastaPastaPastaPastaPasta commented Dec 9, 2024

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

m_peer_mutex is a great use case for a shared mutex; it's private, well contained scope, and 99% of usages are for the read case. The only time we hold an exclusive lock is when adding or removing a peer, which happens rarely compared to all other usages.

On latest nightly, this mutex only represents about 2% of contention (all in PeerRef PeerManagerImpl::GetPeerRef), however, I intend to move over mnauth / verified protx related stuff over into PeerManager, and I expect that will result in significantly more contention over m_peer_mutex, making this change more valuable.

I expect this will reduce contention over this mutex to ~0

What was done?

How Has This Been Tested?

On testnet

Breaking Changes

Checklist:

Go over all the following points, and put an x in all the boxes that apply.

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

@PastaPastaPastaPastaPastaPasta added this to the 22.1 milestone Dec 9, 2024
@DashCoreAutoGuix

Copy link
Copy Markdown

Guix Automation has began to build this PR tagged as v22.1.0-devpr6468.fba3a04a. A new comment will be made when the image is pushed.

@DashCoreAutoGuix

Copy link
Copy Markdown

Guix Automation has completed; a release should be present here: https://github.com/dashpay/dash-dev-branches/releases/tag/v22.1.0-devpr6468.fba3a04a. The image should be on dockerhub soon.

@PastaPastaPasta
PastaPastaPasta marked this pull request as draft December 9, 2024 20:00
@PastaPastaPasta
PastaPastaPastaforce-pushed the perf-shared-mutex-m_peer_map branch from fba3a04 to f044507CompareOctober 21, 2025 19:22
@github-actions

github-actionsBot commented Oct 21, 2025

Copy link
Copy Markdown

✅ No Merge Conflicts Detected

This PR currently has no conflicts with other open PRs.

@PastaPastaPasta
PastaPastaPasta marked this pull request as ready for review October 21, 2025 19:23
@DashCoreAutoGuix

Copy link
Copy Markdown

Guix Automation has began to build this PR tagged as v23.0.0-devpr6468.f0445074. A new comment will be made when the image is pushed.

@PastaPastaPastaPastaPastaPasta modified the milestones: 22.1, 23.1Oct 21, 2025
@PastaPastaPasta

Copy link
Copy Markdown
MemberAuthor

Below is the aggregated contention data I collected on masternode-1 on testnet during 2 minutes of ~ 8tps, so lots of tx locking happening. As you can see, m_peer_mutex does have significant contention over it.

Lock NameLocationCountTotal (μs)Avg (μs)Max (μs)Nodes
connman.m_nodes_mutexnet.cpp:48074342948287218.4380871
m_peer_mutexnet_processing.cpp:1731920258870281.4134181
m_nodes_mutexnet.cpp:2087571186587326.8386711
::cs_mainllmq/quorums.cpp:1211513280406546.6132491
m_nodes_mutex./net.h:1365397230037579.4465231
cs_mainnet_processing.cpp:546823439956170.860821
pnode->cs_vSendnet.cpp:24821711562991.458531
::cs_mainvalidation.cpp:5942153954062.48821
::cs_mainllmq/quorums.cpp:5238523442275.878721
mutexMsgProcnet.cpp:265175324543.322151
cs_mainindex/base.cpp:3396544940691.489261
pnode->cs_vSendnet.cpp:474261393364.513711
m_peer_mutexnet_processing.cpp:2476617407121.412721
m_nodes_mutexnet.cpp:300636780912169.250971
m_send_mutexnet.cpp:1622326337198.055151
cs_mainnet_processing.cpp:5917316537210.927261
m_nodes_mutexnet.cpp:220627823.0171
::cs_maininstantsend/instantsend.cpp:272225858266.310231
cs_dbinstantsend/db.cpp:240222790126.84931
cs_cachespork.cpp:2441972538.24641

Also; when running a local functional test for testing is lock latency, I saw this contention (contention detected 563 times):

m_peer_mutex net_processing.cpp:1731 563

After this change, there is no contention over the lock.

@coderabbitai

coderabbitaiBot commented Oct 21, 2025

Copy link
Copy Markdown

Walkthrough

This change replaces the exclusive Mutex protecting PeerManagerImpl::m_peer_map with a SharedMutex (mutable SharedMutex m_peer_mutex). Read-only accesses (including GetPeerRef and other iteration/read sites such as SendPings, AskPeersForTransaction, RelayInv, RelayDSQ, RelayRecoveredSig, RelayAddress, and relay-related paths) now acquire READ_LOCK(m_peer_mutex) instead of LOCK. Mutating operations that modify m_peer_map or related state remain guarded by exclusive LOCK. Overall control flow is preserved; the intent is to allow concurrent readers while keeping writers exclusive.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
actor Reader1 as Reader Thread A
actor Reader2 as Reader Thread B
actor Writer as Writer Thread
participant PeerMgr as PeerManagerImpl
participant Mutex as m_peer_mutex

Note over Reader1,Reader2: Old flow (exclusive lock)
Reader1->>Mutex: LOCK()
Mutex-->>Reader1: acquired (exclusive)
Reader1->>PeerMgr: read m_peer_map
Reader1->>Mutex: UNLOCK()
Reader2->>Mutex: LOCK()
Mutex-->>Reader2: acquired (exclusive)
Reader2->>PeerMgr: read m_peer_map
Reader2->>Mutex: UNLOCK()

Note over Reader1,Reader2,Writer: New flow (shared locks for readers)
Reader1->>Mutex: READ_LOCK()
Mutex-->>Reader1: acquired (shared)
Reader2->>Mutex: READ_LOCK()
Mutex-->>Reader2: acquired (shared)
Reader1->>PeerMgr: read m_peer_map
Reader2->>PeerMgr: read m_peer_map
Writer->>Mutex: LOCK()
Mutex-->>Writer: waits until readers release
Reader1->>Mutex: READ_UNLOCK()
Reader2->>Mutex: READ_UNLOCK()
Writer->>Mutex: acquired (exclusive)
Writer->>PeerMgr: modify m_peer_map
Writer->>Mutex: UNLOCK()

Note right of Mutex: Colored blocks — shared reads (green), exclusive writes (orange)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Areas to double-check:
    • Sites converted to READ_LOCK (ensure none actually perform hidden mutations or call methods that mutate peer state).
    • GetPeerRef changes and any code paths that depend on upgrading a read lock to a write lock (no lock upgrades introduced).
    • Any iterator usage lifetimes while holding shared lock (ensure objects referenced remain valid for lock duration).

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check nameStatusExplanation
Title Check✅ PassedThe PR title "perf: make m_peer_mutex a SharedMutex" directly and specifically describes the main change in the changeset, which is replacing the synchronization primitive guarding the peer map from a plain Mutex to a SharedMutex in PeerManagerImpl. The title is concise, clear, and unambiguous, making it immediately understandable to someone reviewing the repository history. It appropriately uses the "perf:" prefix to indicate this is a performance optimization.
Description Check✅ PassedThe PR description is clearly related to the changeset, explaining that m_peer_mutex is well-suited to be a shared mutex because 99% of usages are read-only and exclusive locks occur only rarely when adding or removing peers. It provides concrete motivation for the change, noting that the mutex currently represents about 2% of contention on the latest nightly and that future planned changes to move mnauth/verified protx functionality into PeerManager will increase contention further. The description directly addresses the optimization being implemented in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/net_processing.cpp (1)

2515-2532: Fix lifetime hazard: raw Peer used after releasing m_peer_mutex.*

You store Peer* in best[...] under a shared lock, release the lock, then call PushAddress(*best[i].second, …). If a peer is removed concurrently, this can become a dangling pointer (UAF). Capture PeerRef instead to keep the object alive post-unlock.

Apply this diff:

- std::array<std::pair<uint64_t, Peer*>, 2> best{{{0, nullptr}, {0, nullptr}}};+ std::array<std::pair<uint64_t, PeerRef>, 2> best{{{0, {}}, {0, {}}}};
@@
- best[i] = std::make_pair(hashKey, peer.get());+ best[i] = std::make_pair(hashKey, peer);
@@
- for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {- PushAddress(*best[i].second, addr, insecure_rand);- }+ for (unsigned int i = 0; i < nRelayNodes && best[i].first != 0; i++) {+ PushAddress(*best[i].second, addr, insecure_rand);+ }

This mirrors the safe pattern used elsewhere (e.g., collecting PeerRefs in AskPeersForTransaction) and removes the UAF risk.

🧹 Nitpick comments (1)
src/net_processing.cpp (1)

2370-2375: Shrink critical section to reduce contention (optional).

Current loops hold m_peer_mutex (shared) while acquiring per‑peer locks and doing work. Consider:

  • Collect std::vector<PeerRef> under READ_LOCK, then release m_peer_mutex and process each peer (call GetInvRelay(), PushInv(), etc.).
  • This reduces time readers hold m_peer_mutex and lets writers (insert/remove) proceed sooner.

Also applies to: 2463-2471, 2476-2482

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5ce1f0 and f0445074a18e4961f3be8880dbc8a7268bcd1f4e.

📒 Files selected for processing (1)
  • src/net_processing.cpp (9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/net_processing.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: win64-build / Build source
  • GitHub Check: Lint / Run linters
  • GitHub Check: arm-linux-gnueabihf / Build depends
  • GitHub Check: x86_64-pc-linux-gnu / Build depends
🔇 Additional comments (4)
src/net_processing.cpp (4)

1731-1734: GetPeerRef under shared lock — LGTM.

Read-only map lookup with READ_LOCK(m_peer_mutex) is correct; returns PeerRef to extend lifetime post-unlock.


2304-2306: SendPings: shared lock + atomic write — LGTM.

Iterating under READ_LOCK and setting std::atomic<bool> m_ping_queued is safe.


2314-2324: AskPeersForTransaction: shared lock to collect PeerRefs — LGTM.

Collecting PeerRefs under READ_LOCK then acting under cs_main avoids holding m_peer_mutex across downstream locks.


2382-2395: RelayDSQ: shared lock usage — LGTM.

Read-only iteration and deferring sends via nodes_send_all after unlocking is sound.

@@ -819,7 +819,7 @@ class PeerManagerImpl final : public PeerManager

/** Protects m_peer_map. This mutex must not be locked while holding a lock
* on any of the mutexes inside a Peer object. */
mutable Mutex m_peer_mutex;
mutable SharedMutex m_peer_mutex;

@coderabbitaicoderabbitaiBotOct 21, 2025

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

SharedMutex swap looks good; confirm macro support/fairness.

Change to SharedMutex is appropriate for read-heavy access. Please confirm:

  • READ_LOCK(...) is defined by our sync wrappers and mapped to a shared-lock type.
  • No writer starvation policy surprises on our platforms (clang/gcc toolchains).

🏁 Script executed:

#!/bin/bash# Ensure shared-lock macros/types exist and are used consistently
rg -nP '(^|[^A-Z_])READ_LOCK\s*\(' --type=cpp
rg -nP 'class|using\s+SharedMutex|struct\s+SharedMutex' --type=cpp
rg -nP 'LOCK_SHARED\s*\(' --type=cpp

Length of output: 205545


I can confirm the macro support is in place. Now I need to verify the actual code at the flagged lines to assess the lifetime hazard mentioned in the scratchpad.

I need to verify the actual code at the flagged lines to assess the lifetime hazard mentioned in the scratchpad.


🏁 Script executed:

#!/bin/bash# Inspect RelayAddress and _RelayTransaction code context for potential UAF issuesecho"=== RelayAddress context (line 2515-2528) ===" sed -n '2510,2535p' src/net_processing.cpp
echo""echo"=== _RelayTransaction context (line 2463-2471) ===" sed -n '2458,2480p' src/net_processing.cpp
echo""echo"=== Check if PeerRef is defined/used ===" rg -n "struct PeerRef|class PeerRef|using PeerRef" src/net_processing.cpp

Length of output: 1797


🏁 Script executed:

#!/bin/bash# Inspect RelayAddress and _RelayTransaction code context for potential UAF issuesecho"=== RelayAddress context (line 2515-2528) ===" sed -n '2510,2535p' src/net_processing.cpp
echo""echo"=== _RelayTransaction context (line 2463-2471) ===" sed -n '2458,2480p' src/net_processing.cpp
echo""echo"=== Check if PeerRef is used and defined ===" rg -n "PeerRef|using.*Ref" src/net_processing.cpp | head -20

Length of output: 2935


Fix the lifetime hazard in RelayAddress where raw peer pointers are used after releasing the lock.

The READ_LOCK(...) macro is properly supported (defined at src/sync.h:343 and mapped to ReadLock<decltype(cs)> on std::shared_mutex). However, RelayAddress stores raw Peer* pointers in the best array at line 2522, then uses them after the lock scope ends at line 2532. If a peer is removed from m_peer_map between lock release and the PushAddress call, this becomes a use-after-free.

Lines 2515–2528 (RelayAddress):
Capture PeerRef (shared_ptr, line 439) instead of raw pointers to extend peer lifetime beyond the lock. Replace peer.get() with the shared_ptr directly, or collect into std::vector<PeerRef> like AskPeersForTransaction does (line 2310).

The other READ_LOCK sites (_RelayTransaction, RelayRecoveredSig) are safe because all peer access occurs within the lock scope.

🤖 Prompt for AI Agents
src/net_processing.cpp lines 2515-2528: RelayAddress currently collects raw
Peer* pointers into `best` while holding the READ_LOCK, then uses them after the
lock is released causing a potential use-after-free; change the collection to
capture PeerRef (the shared_ptr alias used for peers) instead of raw pointers
(e.g., gather a std::vector<PeerRef> while inside the READ_LOCK), replace later
uses of `peer.get()` with the stored PeerRef, and then call PushAddress/other
post-lock APIs using the preserved PeerRef so peers remain alive after the lock
is released.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look at this again;

For the first case, it appears to me the READ_LOCK is held through the destruction of the best array. I don't see a UAF.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, something went wrong! Please try again later. 🐰 💔

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look at this again;

For the first case, it appears to me the READ_LOCK is held through the destruction of the best array. I don't see a UAF.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PastaPastaPasta You’re right — in RelayAddress the READ_LOCK(m_peer_mutex) is acquired in the function scope and remains held through both loops, including the PushAddress calls. With the shared lock held, m_peer_map cannot be modified by writers, so the Peer* captured into best stays valid during its use. No UAF there. My earlier concern was unfounded — thanks for the nudge.

Optional: if you ever want to reduce the time m_peer_mutex is held, you could collect PeerRef entries under the read lock, release it, then call PushAddress on those refs. Not necessary for correctness here, just a potential micro-optimization.

@DashCoreAutoGuix

Copy link
Copy Markdown

Guix Automation has completed; a release should be present here: https://github.com/dashpay/dash-dev-branches/releases/tag/v23.0.0-devpr6468.f0445074. The image should be on dockerhub soon.

kwvg
kwvg previously approved these changes Oct 23, 2025

@kwvgkwvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK f0445074a18e4961f3be8880dbc8a7268bcd1f4e

Comment threadsrc/net_processing.cpp
Comment threadsrc/net_processing.cpp
Comment threadsrc/net_processing.cpp
@github-actions

Copy link
Copy Markdown

This pull request has conflicts, please rebase.

knst
knst previously approved these changes Oct 26, 2025

@knstknst left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK f0445074a18e4961f3be8880dbc8a7268bcd1f4e (need to rebase)

@PastaPastaPasta
PastaPastaPasta dismissed stale reviews from knst and kwvg via 43fd60fOctober 26, 2025 17:05
@PastaPastaPasta
PastaPastaPastaforce-pushed the perf-shared-mutex-m_peer_map branch from f044507 to 43fd60fCompareOctober 26, 2025 17:05

@knstknst left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-utACK 43fd60f

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
src/net_processing.cpp (6)

2293-2298: SendPings: read-lock + atomic write is safe; consider shortening lock scope.

Optional: collect PeerRef under the read lock, then set m_ping_queued outside to minimize time holding m_peer_mutex.

- READ_LOCK(m_peer_mutex);- for(auto& it : m_peer_map) it.second->m_ping_queued = true;+ std::vector<PeerRef> peers;+ {+ READ_LOCK(m_peer_mutex);+ peers.reserve(m_peer_map.size());+ for (const auto& [_, peer] : m_peer_map) peers.emplace_back(peer);+ }+ for (const auto& peer : peers) {+ peer->m_ping_queued = true;+ }

2361-2366: RelayInv: avoid holding m_peer_mutex while pushing to peers.

Optional: mirror the AskPeersForTransaction pattern to shorten lock time.

- READ_LOCK(m_peer_mutex);- for (const auto& [_, peer] : m_peer_map) {- if (!peer->GetInvRelay()) continue;- PushInv(*peer, inv);- }+ std::vector<PeerRef> peers;+ {+ READ_LOCK(m_peer_mutex);+ peers.reserve(m_peer_map.size());+ for (const auto& [_, peer] : m_peer_map) peers.emplace_back(peer);+ }+ for (const auto& peer : peers) {+ if (!peer->GetInvRelay()) continue;+ PushInv(*peer, inv);+ }

2370-2393: RelayDSQ: consistent with shared lock; optionally move INV path out of lock.

You already defer DSQ::ALL sends via NodeId vector. For DSQ::INV, consider collecting PeerRef under READ_LOCK, then PushInv after releasing it to further reduce contention.


2453-2464: _RelayTransaction: shorten lock scope while broadcasting.

Optional: collect PeerRef, then PushInv outside the READ_LOCK.

- READ_LOCK(m_peer_mutex);- for(auto& it : m_peer_map) {- Peer& peer = *it.second;- auto tx_relay = peer.GetTxRelay();- if (!tx_relay) continue;- PushInv(peer, inv);- };+ std::vector<PeerRef> peers;+ {+ READ_LOCK(m_peer_mutex);+ peers.reserve(m_peer_map.size());+ for (const auto& [_, peer] : m_peer_map) peers.emplace_back(peer);+ }+ for (const auto& peer : peers) {+ if (auto tx_relay = peer->GetTxRelay()) {+ PushInv(*peer, inv);+ }+ }

2468-2475: RelayRecoveredSig: same optional lock-narrowing.

Collect PeerRef under READ_LOCK, then PushInv after releasing it.

- READ_LOCK(m_peer_mutex);- for (const auto& [_, peer] : m_peer_map) {- if (peer->m_wants_recsigs) {- PushInv(*peer, inv);- }- }+ std::vector<PeerRef> peers;+ {+ READ_LOCK(m_peer_mutex);+ peers.reserve(m_peer_map.size());+ for (const auto& [_, peer] : m_peer_map) peers.emplace_back(peer);+ }+ for (const auto& peer : peers) {+ if (peer->m_wants_recsigs) {+ PushInv(*peer, inv);+ }+ }

2504-2525: RelayAddress: current approach is safe; optional lifetime-friendly tweak.

With READ_LOCK held across both loops, raw Peer* remains valid — no UAF. If you want to release m_peer_mutex before PushAddress, store PeerRef instead of raw pointers.

- std::array<std::pair<uint64_t, Peer*>, 2> best{{{0, nullptr}, {0, nullptr}}};+ std::array<std::pair<uint64_t, PeerRef>, 2> best{{{0, {}}, {0, {}}}};
@@
- READ_LOCK(m_peer_mutex);- for (auto& [id, peer] : m_peer_map) {+ {+ READ_LOCK(m_peer_mutex);+ for (const auto& [id, peer] : m_peer_map) {
@@
- best[i] = std::make_pair(hashKey, peer.get());+ best[i] = std::make_pair(hashKey, peer);
@@
- };+ } // release m_peer_mutex
@@
- PushAddress(*best[i].second, addr, insecure_rand);+ PushAddress(*best[i].second, addr, insecure_rand);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0445074a18e4961f3be8880dbc8a7268bcd1f4e and 43fd60f.

📒 Files selected for processing (1)
  • src/net_processing.cpp (9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.{cpp,h,cc,cxx,hpp}

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.{cpp,h,cc,cxx,hpp}: Dash Core C++ codebase must be written in C++20 and require at least Clang 16 or GCC 11.1
Dash uses unordered_lru_cache for efficient caching with LRU eviction

Files:

  • src/net_processing.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: linux64-build / Build source
  • GitHub Check: arm-linux-build / Build source
  • GitHub Check: linux64_nowallet-build / Build source
  • GitHub Check: x86_64-apple-darwin / Build depends
  • GitHub Check: x86_64-pc-linux-gnu_multiprocess / Build depends
  • GitHub Check: Lint / Run linters
  • GitHub Check: x86_64-w64-mingw32 / Build depends
  • GitHub Check: x86_64-pc-linux-gnu / Build depends
  • GitHub Check: Lint / Run linters
🔇 Additional comments (3)
src/net_processing.cpp (3)

815-824: SharedMutex swap is correct for read-dominant access.

m_peer_map still uses exclusive LOCKs on mutation (InitializeNode/RemovePeer). An appropriate upgrade.


1724-1729: GetPeerRef now uses READ_LOCK — safe lifetime.

Returning PeerRef under a shared lock, then releasing, is correct per the comment that a shared_ptr keeps the peer alive.


2304-2315: AskPeersForTransaction: good pattern.

Collecting PeerRef under READ_LOCK and using them after releasing the lock is exactly the right approach here.

@kwvgkwvg left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 43fd60f

@UdjinM6UdjinM6 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 43fd60f

@PastaPastaPasta
PastaPastaPasta merged commit b82450a into dashpay:developOct 26, 2025
61 of 66 checks passed
@PastaPastaPasta
PastaPastaPasta deleted the perf-shared-mutex-m_peer_map branch October 26, 2025 18:08
PastaPastaPasta added a commit that referenced this pull request Nov 20, 2025
…ntion
e918729 refactor: add CacheTipHeight, improve cache API safety (UdjinM6)
493d077 refactor: simplify optional usage (pasta)
0e1f114 instantsend: cache heights and reduce cs_main contention (pasta)
Pull request description:
## Issue being fixed or feature implemented
Today's testing on testnet showed under load, cs_main was a major contender:
(however, note the major contenders from before #6912 / #6468 are now significantly reduced)
```
========================================================================================================================
TOP 40 LOCATIONS BY TOTAL CONTENTION TIME (AGGREGATE)
========================================================================================================================
Lock Name Location Count Total(μs) Avg(μs) Max(μs) Nodes
------------------------------------------------------------------------------------------------------------------------
cs llmq/signing_shares.cpp:507 11952 81357095 6807.0 299734 8
::cs_main instantsend/signing.cpp:210 75241 62985844 837.1 93240 8
cs_main index/base.cpp:340 29584 27040349 914.0 1613365 8
cs_main net_processing.cpp:5462 69291 19784473 285.5 1329191 8
cs llmq/signing_shares.cpp:1732 2355 17466035 7416.6 127471 8
::cs_main llmq/quorums.cpp:1214 38975 15478543 397.1 883884 8
m_nodes_mutex net.cpp:2043 33457 14736059 440.4 31622 8
::cs_main llmq/quorums.cpp:526 10587 10577503 999.1 2083501 8
pnode->cs_vSend net.cpp:2435 68528 9671050 141.1 37265 8
cs_main net_processing.cpp:4196 4945 8608157 1740.8 1326146 8
::cs_main instantsend/instantsend.cpp:272 5602 8416269 1502.4 1260997 8
cs txmempool.cpp:1319 7942 8059684 1014.8 356199 8
::cs_main validation.cpp:3747 309 6468144 20932.5 1225274 8
::cs_main validation.cpp:6009 21855 3393152 155.3 228195 8
pnode->cs_vSend net.cpp:4709 7532 2299813 305.3 41543 8
m_nodes_mutex ./net.h:1374 6229 1998506 320.8 15593 8
inv_relay->m_tx_inventory_mutex net_processing.cpp:1169 7687 1871859 243.5 16292 8
cs_db instantsend/db.cpp:239 4323 1527297 353.3 20779 8
cs_cache spork.cpp:244 18083 1331472 73.6 27722 8
::cs_main chainlock/signing.cpp:58 98 1312496 13392.8 1273691 8
cs_db ./instantsend/db.h:139 4381 1305738 298.0 29777 8
mutexMsgProc net.cpp:2604 4877 1243737 255.0 24641 8
```
- Add unordered_lru_cache for block heights in CInstantSendManager
- Provide GetTipHeight() (non-optional) and GetBlockHeight() helpers
- Use cache in InstantSendSigner::CheckCanLock
- Cache-first for cycleHash height in ISLOCK message path and batch verify
- Cache-first mined-height for HasChainLock and WriteInstantSendLockMined
## What was done?
## How Has This Been Tested?
## Breaking Changes
_Please describe any breaking changes your code introduces_
## Checklist:
_Go over all the following points, and put an `x` in all the boxes that apply._
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e tests
- [ ] I have made corresponding changes to the documentation
- [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_
ACKs for top commit:
UdjinM6:
utACK e918729
Tree-SHA512: 1ea45e92c717dbefe5625bba3b27612b9ffbb21ea106d11005d78836fbe6ce00503e3f3fdb927fb405b09fd795f50137ca2beb07351bf94488d2af5694e543ba
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@PastaPastaPasta@DashCoreAutoGuix@knst@UdjinM6@kwvg