Would be good to not redundantly copy the lock annotation from the class declaration to the member implementation. Otherwise it may not result in a compile failure if a new lock requirement is added to the member implementation, but not the class declaration.
scripted-diff: Remove redundant lock annotations in net processing #21188
pull MarcoFalke wants to merge 1 commits into bitcoin:master from MarcoFalke:2102-netProcNoShadow changing 1 files +8 −8-
MarcoFalke commented at 12:43 PM on February 15, 2021: member
-
fafddfadda
scripted-diff: Remove shadowing lock annotations
Can be reviewed with --word-diff-regex=. -BEGIN VERIFY SCRIPT- sed -i --regexp-extended 's/(PeerManagerImpl::.*\)).*LOCKS_.*\)/\1/g' ./src/net_processing.cpp -END VERIFY SCRIPT-
-
MarcoFalke commented at 12:44 PM on February 15, 2021: member
Fixes #20942 (review)
-
MarcoFalke commented at 12:45 PM on February 15, 2021: member
Too bad the compiler can't error on those by itself
- DrahtBot added the label P2P on Feb 15, 2021
- DrahtBot added the label Refactoring on Feb 15, 2021
- hebasto approved
-
hebasto commented at 3:06 PM on February 15, 2021: member
ACK fafddfadda0c77876ba764c5b65ee5fa8e53a5e0
Out of curiosity, is the code base is free from similar issues in other translation units?
-
MarcoFalke commented at 3:19 PM on February 15, 2021: member
I don't think so. This is all my hacky grep could find.
$ git grep --extended-regexp --ignore-case ' [a-z0-9]+::[a-z0-9]+\([^()]*\).*LOCKS_' src/net_processing.cpp:bool PeerManagerImpl::MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) src/net_processing.cpp:bool PeerManagerImpl::MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const CBlockIndex* pindex, std::list<QueuedBlock>::iterator** pit) EXCLUSIVE_LOCKS_REQUIRED(cs_main) src/net_processing.cpp:void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) src/net_processing.cpp:bool PeerManagerImpl::TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main) src/net_processing.cpp:void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<const CBlockIndex*>& vBlocks, NodeId& nodeStaller) EXCLUSIVE_LOCKS_REQUIRED(cs_main) src/net_processing.cpp:bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_main) src/net_processing.cpp:CTransactionRef PeerManagerImpl::FindTxForGetData(const CNode& peer, const GenTxid& gtxid, const std::chrono::seconds mempool_req, const std::chrono::seconds now) LOCKS_EXCLUDED(cs_main) src/net_processing.cpp:void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(!cs_main, peer.m_getdata_requests_mutex) src/qt/transactiondesc.cpp: strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", status.blocks_to_maturity) + ")"; -
jonatack commented at 3:26 PM on February 15, 2021: member
Light utACK fafddfadda0c77876ba764c5b65ee5fa8e53a5e0 verified that the removed annotations in the definitions correspond to those in their respective declarations
-
amitiuttarwar commented at 12:22 AM on February 16, 2021: contributor
I don't understand the issue. How can I reproduce it?
I interpreted the issue to be that we would not get a compiler warning under the following circumstance:
- header declaration of function FooBar has annotation EXCLUSIVE_LOCKS_REQUIRED(lock1, lock2)
- FooBar definition has annotation EXCLUSIVE_LOCKS_REQUIRED(lock1)
- call site acquires lock1 then calls FooBar
- issue: we do not get a compiler warning that lock2 was not acquired before calling FooBar
I tried it out by doing the following:
- on master,
TipMayBeStale()hasEXCLUSIVE_LOCKS_REQUIRED(cs_main)on both the function declaration and the function definition - I added a new mutex
RecursiveMutex cs_other;and updated only the declaration ofTipMayBeStale()to beEXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_other); - I did not update the annotations in the definition of
TipMayBeStale()or the call siteCheckForStaleTipAndEvictPeers.
full diff:
- bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main); + RecursiveMutex cs_other; + bool TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_other);- I expected that the issue would be that we would not get a compiler warning even though the call site didn't take
cs_other.
However, I did get a compiler warning:
net_processing.cpp:4284:110: warning: calling function 'TipMayBeStale' requires holding mutex 'cs_other' exclusively [-Wthread-safety-analysis]So, I don't understand the context of what we are trying to prevent / fix. -
MarcoFalke commented at 7:27 AM on February 16, 2021: member
Ah good catch. Looks like it is not shadowing. Though, I still think the patch is correct. You can test by adding
cs_otherto theTipMayBeStaleimplementation. Then move a function that callsTipMayBeStaleto before its implementation. Compilation won't print any warning. The diff I used:diff --git a/src/net_processing.cpp b/src/net_processing.cpp index c782498e14..bb5385f8de 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -778,7 +778,33 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid) EXCLUS } } -bool PeerManagerImpl::TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(cs_main) +void PeerManagerImpl::CheckForStaleTipAndEvictPeers() +{ + LOCK(cs_main); + + int64_t time_in_seconds = GetTime(); + + EvictExtraOutboundPeers(time_in_seconds); + + if (time_in_seconds > m_stale_tip_check_time) { + // Check whether our tip is stale, and if so, allow using an extra + // outbound peer + if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale()) { + LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - m_last_tip_update); + m_connman.SetTryNewOutboundPeer(true); + } else if (m_connman.GetTryNewOutboundPeer()) { + m_connman.SetTryNewOutboundPeer(false); + } + m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; + } + + if (!m_initial_sync_finished /*&& CanDirectFetch(m_chainparams.GetConsensus())*/) { + m_connman.StartExtraBlockRelayPeers(); + m_initial_sync_finished = true; + } +} + +bool PeerManagerImpl::TipMayBeStale() EXCLUSIVE_LOCKS_REQUIRED(m_recent_confirmed_transactions_mutex) { AssertLockHeld(cs_main); const Consensus::Params& consensusParams = m_chainparams.GetConsensus(); @@ -4269,32 +4295,6 @@ void PeerManagerImpl::EvictExtraOutboundPeers(int64_t time_in_seconds) } } -void PeerManagerImpl::CheckForStaleTipAndEvictPeers() -{ - LOCK(cs_main); - - int64_t time_in_seconds = GetTime(); - - EvictExtraOutboundPeers(time_in_seconds); - - if (time_in_seconds > m_stale_tip_check_time) { - // Check whether our tip is stale, and if so, allow using an extra - // outbound peer - if (!fImporting && !fReindex && m_connman.GetNetworkActive() && m_connman.GetUseAddrmanOutgoing() && TipMayBeStale()) { - LogPrintf("Potential stale tip detected, will try using extra outbound peer (last tip update: %d seconds ago)\n", time_in_seconds - m_last_tip_update); - m_connman.SetTryNewOutboundPeer(true); - } else if (m_connman.GetTryNewOutboundPeer()) { - m_connman.SetTryNewOutboundPeer(false); - } - m_stale_tip_check_time = time_in_seconds + STALE_CHECK_INTERVAL; - } - - if (!m_initial_sync_finished && CanDirectFetch(m_chainparams.GetConsensus())) { - m_connman.StartExtraBlockRelayPeers(); - m_initial_sync_finished = true; - } -} - namespace { class CompareInvMempoolOrder { - MarcoFalke renamed this:
scripted-diff: Remove shadowing lock annotations in net processing
scripted-diff: Remove redundant lock annotations in net processing
on Feb 16, 2021 -
MarcoFalke commented at 7:32 AM on February 16, 2021: member
(Clarified OP and title a bit)
-
amitiuttarwar commented at 7:29 PM on February 16, 2021: contributor
ok, I understand now- if the declaration annotates
EXCLUSIVE_LOCKS_REQUIRED(lock1), then the caller invokes, then the implementation annotatesEXCLUSIVE_LOCKS_REQUIRED(lock1, lock2), we won't get the compiler warnings about lock2. I was able to see this with your diff, @MarcoFalke. thanks!ACK
fafddfadda, confirmed that the annotations removed were all redundant. confirmed the claim of potential issue :)I also checked to see if there were any other annotations with this issue. I didn't find any with the same redundancy problem. However, I found that
CheckInputScriptshas the annotation on the function definition instead of the declaration earlier in the file. I checked that this could lead to a similar issue, so we should change that as well.I think this clarification is worth adding to the
developer-notes, so I'll open a small PR right now. I can add theCheckInputScriptschange there since this PR already has 3 ACKs. - MarcoFalke merged this on Feb 17, 2021
- MarcoFalke closed this on Feb 17, 2021
- MarcoFalke deleted the branch on Feb 17, 2021
- sidhujag referenced this in commit d133ecf091 on Feb 17, 2021
- MarcoFalke referenced this in commit 34d7030063 on Feb 22, 2021
- Fabcien referenced this in commit d63cbfbcbd on May 11, 2022
- Fabcien referenced this in commit 2e57141cb1 on May 11, 2022
- DrahtBot locked this on Aug 16, 2022