net: make CConnman::m_nodes_mutex non-recursive

This change includes `s/RecursiveMutex/Mutex/` and a pile of
annotations to keep the compiler happy after the type change.

Partially resolves: https://github.com/bitcoin/bitcoin/issues/19303
This commit is contained in:
Vasil Dimov
2025-10-15 15:58:29 +02:00
parent aec4fa2de0
commit 11713c9fa9
2 changed files with 105 additions and 41 deletions

View File

@@ -381,6 +381,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect,
bool use_v2transport,
const std::optional<Proxy>& proxy_override)
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
assert(conn_type != ConnectionType::INBOUND);
@@ -1688,6 +1689,8 @@ std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
*/
bool CConnman::AttemptToEvictConnection()
{
AssertLockNotHeld(m_nodes_mutex);
std::vector<NodeEvictionCandidate> vEvictionCandidates;
{
@@ -1736,6 +1739,8 @@ bool CConnman::AttemptToEvictConnection()
}
void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
AssertLockNotHeld(m_nodes_mutex);
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
auto sock = hListenSocket.sock->Accept((struct sockaddr*)&sockaddr, &len);
@@ -1768,6 +1773,8 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
const CService& addr_bind,
const CService& addr)
{
AssertLockNotHeld(m_nodes_mutex);
int nInbound = 0;
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
@@ -1875,6 +1882,7 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
bool CConnman::AddConnection(const std::string& address, ConnectionType conn_type, bool use_v2transport = false)
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
std::optional<int> max_connections;
switch (conn_type) {
@@ -1992,6 +2000,8 @@ void CConnman::DisconnectNodes()
void CConnman::NotifyNumConnectionsChanged()
{
AssertLockNotHeld(m_nodes_mutex);
size_t nodes_size;
{
LOCK(m_nodes_mutex);
@@ -2096,6 +2106,7 @@ Sock::EventsPerSock CConnman::GenerateWaitSockets(std::span<CNode* const> nodes)
void CConnman::SocketHandler()
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_total_bytes_sent_mutex);
Sock::EventsPerSock events_per_sock;
@@ -2226,6 +2237,8 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
{
AssertLockNotHeld(m_nodes_mutex);
for (const ListenSocket& listen_socket : vhListenSocket) {
if (m_interrupt_net->interrupted()) {
return;
@@ -2409,6 +2422,7 @@ void CConnman::DumpAddresses()
void CConnman::ProcessAddrFetch()
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
std::string strDest;
{
@@ -2448,6 +2462,8 @@ void CConnman::StartExtraBlockRelayPeers()
// Return the number of outbound connections that are full relay (not blocks only)
int CConnman::GetFullOutboundConnCount() const
{
AssertLockNotHeld(m_nodes_mutex);
int nRelevant = 0;
{
LOCK(m_nodes_mutex);
@@ -2466,6 +2482,8 @@ int CConnman::GetFullOutboundConnCount() const
// evict some peer that has finished the handshake)
int CConnman::GetExtraFullOutboundCount() const
{
AssertLockNotHeld(m_nodes_mutex);
int full_outbound_peers = 0;
{
LOCK(m_nodes_mutex);
@@ -2480,6 +2498,8 @@ int CConnman::GetExtraFullOutboundCount() const
int CConnman::GetExtraBlockRelayCount() const
{
AssertLockNotHeld(m_nodes_mutex);
int block_relay_peers = 0;
{
LOCK(m_nodes_mutex);
@@ -2513,6 +2533,8 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
{
AssertLockNotHeld(m_nodes_mutex);
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
std::shuffle(nets.begin(), nets.end(), FastRandomContext());
@@ -2529,8 +2551,10 @@ bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std::span<const std::string> seed_nodes)
{
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_reconnections_mutex);
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
FastRandomContext rng;
// Connect to specific addresses
if (!connect.empty())
@@ -2900,6 +2924,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
{
AssertLockNotHeld(m_nodes_mutex);
std::vector<CAddress> ret;
LOCK(m_nodes_mutex);
for (const CNode* pnode : m_nodes) {
@@ -2913,6 +2938,8 @@ std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo(bool include_connected) const
{
AssertLockNotHeld(m_nodes_mutex);
std::vector<AddedNodeInfo> ret;
std::list<AddedNodeParams> lAddresses(0);
@@ -2973,8 +3000,10 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo(bool include_connected) co
void CConnman::ThreadOpenAddedConnections()
{
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_reconnections_mutex);
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
while (true)
{
CountingSemaphoreGrant<> grant(*semAddnode);
@@ -3010,6 +3039,7 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect,
bool use_v2transport,
const std::optional<Proxy>& proxy_override)
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
assert(conn_type != ConnectionType::INBOUND);
@@ -3129,6 +3159,8 @@ Mutex NetEventsInterface::g_msgproc_mutex;
void CConnman::ThreadMessageHandler()
{
AssertLockNotHeld(m_nodes_mutex);
LOCK(NetEventsInterface::g_msgproc_mutex);
while (!flagInterruptMsgProc)
@@ -3168,6 +3200,8 @@ void CConnman::ThreadMessageHandler()
void CConnman::ThreadI2PAcceptIncoming()
{
AssertLockNotHeld(m_nodes_mutex);
static constexpr auto err_wait_begin = 1s;
static constexpr auto err_wait_cap = 5min;
auto err_wait = err_wait_begin;
@@ -3211,6 +3245,7 @@ void CConnman::ThreadI2PAcceptIncoming()
void CConnman::ThreadPrivateBroadcast()
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
size_t addrman_num_bad_addresses{0};
@@ -3640,6 +3675,7 @@ void CConnman::StopThreads()
void CConnman::StopNodes()
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_reconnections_mutex);
if (fAddressesInitialized) {
@@ -3804,6 +3840,8 @@ uint32_t CConnman::GetMappedAS(const CNetAddr& addr) const
void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
{
AssertLockNotHeld(m_nodes_mutex);
vstats.clear();
LOCK(m_nodes_mutex);
vstats.reserve(m_nodes.size());
@@ -3829,6 +3867,7 @@ bool CConnman::DisconnectNode(std::string_view strNode)
bool CConnman::DisconnectNode(const CSubNet& subnet)
{
AssertLockNotHeld(m_nodes_mutex);
bool disconnected = false;
LOCK(m_nodes_mutex);
for (CNode* pnode : m_nodes) {
@@ -3843,6 +3882,7 @@ bool CConnman::DisconnectNode(const CSubNet& subnet)
bool CConnman::DisconnectNode(const CNetAddr& addr)
{
AssertLockNotHeld(m_nodes_mutex);
return DisconnectNode(CSubNet(addr));
}
@@ -4125,6 +4165,8 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
{
AssertLockNotHeld(m_nodes_mutex);
CNode* found = nullptr;
LOCK(m_nodes_mutex);
for (auto&& pnode : m_nodes) {
@@ -4150,6 +4192,7 @@ uint64_t CConnman::CalculateKeyedNetGroup(const CNetAddr& address) const
void CConnman::PerformReconnections()
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_reconnections_mutex);
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
while (true) {

View File

@@ -1154,9 +1154,10 @@ public:
bool Start(CScheduler& scheduler, const Options& options) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !m_added_nodes_mutex, !m_addr_fetches_mutex, !mutexMsgProc);
void StopThreads();
void StopNodes() EXCLUSIVE_LOCKS_REQUIRED(!m_reconnections_mutex);
void Stop() EXCLUSIVE_LOCKS_REQUIRED(!m_reconnections_mutex)
void StopNodes() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_reconnections_mutex);
void Stop() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_reconnections_mutex)
{
AssertLockNotHeld(m_nodes_mutex);
AssertLockNotHeld(m_reconnections_mutex);
StopThreads();
StopNodes();
@@ -1186,7 +1187,7 @@ public:
ConnectionType conn_type,
bool use_v2transport,
const std::optional<Proxy>& proxy_override = std::nullopt)
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_unused_i2p_sessions_mutex);
/// Group of private broadcast related members.
class PrivateBroadcast
@@ -1254,18 +1255,18 @@ public:
friend struct ConnmanTestMsg;
} m_private_broadcast;
bool CheckIncomingNonce(uint64_t nonce);
bool CheckIncomingNonce(uint64_t nonce) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
void ASMapHealthCheck();
// alias for thread safety annotations only, not defined
RecursiveMutex& GetNodesMutex() const LOCK_RETURNED(m_nodes_mutex);
Mutex& GetNodesMutex() const LOCK_RETURNED(m_nodes_mutex);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
void PushMessage(CNode* pnode, CSerializedNetMsg&& msg) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex);
using NodeFn = std::function<void(CNode*)>;
void ForEachNode(const NodeFn& func)
void ForEachNode(const NodeFn& func) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex)
{
LOCK(m_nodes_mutex);
for (auto&& node : m_nodes) {
@@ -1274,7 +1275,7 @@ public:
}
};
void ForEachNode(const NodeFn& func) const
void ForEachNode(const NodeFn& func) const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex)
{
LOCK(m_nodes_mutex);
for (auto&& node : m_nodes) {
@@ -1320,21 +1321,22 @@ public:
void StartExtraBlockRelayPeers();
// Count the number of full-relay peer we have.
int GetFullOutboundConnCount() const;
int GetFullOutboundConnCount() const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
// Return the number of outbound peers we have in excess of our target (eg,
// if we previously called SetTryNewOutboundPeer(true), and have since set
// to false, we may have extra peers that we wish to disconnect). This may
// return a value less than (num_outbound_connections - num_outbound_slots)
// in cases where some outbound connections are not yet fully connected, or
// not yet fully disconnected.
int GetExtraFullOutboundCount() const;
int GetExtraFullOutboundCount() const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
// Count the number of block-relay-only peers we have over our limit.
int GetExtraBlockRelayCount() const;
int GetExtraBlockRelayCount() const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
bool AddNode(const AddedNodeParams& add) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
bool RemoveAddedNode(std::string_view node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
bool AddedNodesContain(const CAddress& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
std::vector<AddedNodeInfo> GetAddedNodeInfo(bool include_connected) const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
std::vector<AddedNodeInfo> GetAddedNodeInfo(bool include_connected) const
EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_nodes_mutex);
/**
* Attempts to open a connection. Currently only used from tests.
@@ -1349,16 +1351,17 @@ public:
* - Max total outbound connection capacity filled
* - Max connection capacity for type is filled
*/
bool AddConnection(const std::string& address, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
bool AddConnection(const std::string& address, ConnectionType conn_type, bool use_v2transport)
EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_unused_i2p_sessions_mutex);
size_t GetNodeCount(ConnectionDirection) const;
size_t GetNodeCount(ConnectionDirection) const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
std::map<CNetAddr, LocalServiceInfo> getNetLocalAddresses() const;
uint32_t GetMappedAS(const CNetAddr& addr) const;
void GetNodeStats(std::vector<CNodeStats>& vstats) const;
bool DisconnectNode(std::string_view node);
bool DisconnectNode(const CSubNet& subnet);
bool DisconnectNode(const CNetAddr& addr);
bool DisconnectNode(NodeId id);
void GetNodeStats(std::vector<CNodeStats>& vstats) const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
bool DisconnectNode(std::string_view node) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
bool DisconnectNode(const CSubNet& subnet) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
bool DisconnectNode(const CNetAddr& addr) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
bool DisconnectNode(NodeId id) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
//! Used to convey which local services we are offering peers during node
//! connection.
@@ -1422,14 +1425,28 @@ private:
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
bool InitBinds(const Options& options);
void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex, !m_unused_i2p_sessions_mutex, !m_reconnections_mutex);
void ThreadOpenAddedConnections() EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex,
!m_nodes_mutex,
!m_reconnections_mutex,
!m_unused_i2p_sessions_mutex);
void AddAddrFetch(const std::string& strDest) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex);
void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_unused_i2p_sessions_mutex);
void ThreadOpenConnections(std::vector<std::string> connect, std::span<const std::string> seed_nodes) EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_added_nodes_mutex, !m_nodes_mutex, !m_unused_i2p_sessions_mutex, !m_reconnections_mutex);
void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc);
void ThreadI2PAcceptIncoming();
void ThreadPrivateBroadcast() EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
void AcceptConnection(const ListenSocket& hListenSocket);
void ProcessAddrFetch() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex,
!m_nodes_mutex,
!m_unused_i2p_sessions_mutex);
void ThreadOpenConnections(std::vector<std::string> connect, std::span<const std::string> seed_nodes)
EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex,
!m_addr_fetches_mutex,
!m_nodes_mutex,
!m_reconnections_mutex,
!m_unused_i2p_sessions_mutex);
void ThreadMessageHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !mutexMsgProc);
void ThreadI2PAcceptIncoming() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
void ThreadPrivateBroadcast() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_unused_i2p_sessions_mutex);
void AcceptConnection(const ListenSocket& hListenSocket) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
/**
* Create a `CNode` object from a socket that has just been accepted and add the node to
@@ -1442,10 +1459,11 @@ private:
void CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
NetPermissionFlags permission_flags,
const CService& addr_bind,
const CService& addr);
const CService& addr)
EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
void DisconnectNodes() EXCLUSIVE_LOCKS_REQUIRED(!m_reconnections_mutex, !m_nodes_mutex);
void NotifyNumConnectionsChanged();
void NotifyNumConnectionsChanged() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
/** Return true if the peer is inactive and should be disconnected. */
bool InactivityCheck(const CNode& node, NodeClock::time_point now) const;
@@ -1459,7 +1477,7 @@ private:
/**
* Check connected and listening sockets for IO readiness and process them accordingly.
*/
void SocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc);
void SocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_total_bytes_sent_mutex, !mutexMsgProc);
/**
* Do the read/write for connected sockets that are ready for IO.
@@ -1474,7 +1492,8 @@ private:
* Accept incoming connections, one from each read-ready listening socket.
* @param[in] events_per_sock Sockets that are ready for IO.
*/
void SocketHandlerListening(const Sock::EventsPerSock& events_per_sock);
void SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
void ThreadSocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc, !m_nodes_mutex, !m_reconnections_mutex);
void ThreadDNSAddressSeed() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_nodes_mutex);
@@ -1488,7 +1507,7 @@ private:
* @param[in] host String of the form "host[:port]", e.g. "localhost" or "localhost:8333" or "1.2.3.4:8333".
* @return true if connected to `host`.
*/
bool AlreadyConnectedToHost(std::string_view host) const;
bool AlreadyConnectedToHost(std::string_view host) const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
/**
* Determine whether we're already connected to a given address:port.
@@ -1497,14 +1516,14 @@ private:
* @param[in] addr_port Address and port to check.
* @return true if connected to addr_port.
*/
bool AlreadyConnectedToAddressPort(const CService& addr_port) const;
bool AlreadyConnectedToAddressPort(const CService& addr_port) const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
/**
* Determine whether we're already connected to a given address.
*/
bool AlreadyConnectedToAddress(const CNetAddr& addr) const;
bool AlreadyConnectedToAddress(const CNetAddr& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
bool AttemptToEvictConnection();
bool AttemptToEvictConnection() EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
/**
* Open a new P2P connection.
@@ -1522,7 +1541,7 @@ private:
ConnectionType conn_type,
bool use_v2transport,
const std::optional<Proxy>& proxy_override)
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_unused_i2p_sessions_mutex);
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const;
@@ -1548,7 +1567,7 @@ private:
/**
* Return vector of current BLOCK_RELAY peers.
*/
std::vector<CAddress> GetCurrentBlockRelayOnlyConns() const;
std::vector<CAddress> GetCurrentBlockRelayOnlyConns() const EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
/**
* Search for a "preferred" network, a reachable network to which we
@@ -1560,7 +1579,7 @@ private:
*
* @return bool Whether a preferred network was found.
*/
bool MaybePickPreferredNetwork(std::optional<Network>& network);
bool MaybePickPreferredNetwork(std::optional<Network>& network) EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex);
// Whether the node should be passed out in ForEach* callbacks
static bool NodeFullyConnected(const CNode* pnode);
@@ -1604,7 +1623,7 @@ private:
mutable Mutex m_added_nodes_mutex;
std::vector<CNode*> m_nodes GUARDED_BY(m_nodes_mutex);
std::list<CNode*> m_nodes_disconnected;
mutable RecursiveMutex m_nodes_mutex;
mutable Mutex m_nodes_mutex;
std::atomic<NodeId> nLastNodeId{0};
unsigned int nPrevNodeCount{0};
@@ -1790,7 +1809,8 @@ private:
std::list<ReconnectionInfo> m_reconnections GUARDED_BY(m_reconnections_mutex);
/** Attempt reconnections, if m_reconnections non-empty. */
void PerformReconnections() EXCLUSIVE_LOCKS_REQUIRED(!m_reconnections_mutex, !m_unused_i2p_sessions_mutex);
void PerformReconnections()
EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_reconnections_mutex, !m_unused_i2p_sessions_mutex);
/**
* Cap on the size of `m_unused_i2p_sessions`, to ensure it does not
@@ -1806,6 +1826,7 @@ private:
{
public:
explicit NodesSnapshot(const CConnman& connman, bool shuffle)
EXCLUSIVE_LOCKS_REQUIRED(!connman.m_nodes_mutex)
{
{
LOCK(connman.m_nodes_mutex);