Skip to content

Conversation

@jonatack
Copy link
Member

@jonatack jonatack commented Jan 31, 2022

No longer re-acquire lock cs_main during IBD in CChainState::IsInitialBlockDownload(), as most of its callers already hold the lock. Have these callers invoke non-locking function CChainState::IsIBD() instead. Apply thread safety analysis to the IBD functions. Retains the post-IBD optimization latch for the callers that do not hold the lock. Makes explicit which callers hold cs_main, and which do not.

@jonatack
Copy link
Member Author

Noticed while reviewing #24178.

@sdaftuar
Copy link
Member

sdaftuar commented Jan 31, 2022

I recall that the reason IsIBD was written like this:

bool CChainState::IsInitialBlockDownload() const
{
    // Optimization: pre-test latch before taking the lock.
    if (m_cached_finished_ibd.load(std::memory_order_relaxed))
        return false;

    LOCK(cs_main);

is so that in the common case -- which is that we've already latched to false -- that we can respond without ever acquiring cs_main inside this function. See #8007

Maybe there's a better refactor that could be done so that we can reduce locking contention on cs_main further, but this patch would have all the call sites grab the lock in order to even call IsIBD(), which would add more cs_main lock acquisitions in general -- and that seems counter to where we want to end up?

Is the motivation for this patch to reduce the uses of cs_main as a recursive mutex, and thereby get to a better design? If so that sounds like a good direction, but I think it'd be better if we avoided adding extra lock acquisitions (and holding cs_main longer) in order to get there. For instance it seems like with this patch some of the validation callbacks will now be invoked while holding cs_main the whole time? That seems undesirable.

@jonatack
Copy link
Member Author

jonatack commented Jan 31, 2022

Yes, the trade-off this pull makes EDIT: is no longer made, thanks to review feedback. foregoing the optimization for a half dozen callers that don't already hold cs_main, in favor of avoiding re-taking the cs_main lock for the 18 callers that do already hold it, by moving the lock-taking to the half dozen callers (but just for the call itself, so essentially only the optimization is affected unless I am missing something).

Thanks for the link to #8007. It looks like the proportion of callers that hold the lock has since increased.

@jonatack jonatack force-pushed the remove-cs_main-lock-from-IsInitialBlockDownload branch from 9adeef8 to 5159d2c Compare January 31, 2022 19:33
@jamesob
Copy link
Contributor

jamesob commented Jan 31, 2022

For instance it seems like with this patch some of the validation callbacks will now be invoked while holding cs_main the whole time? That seems undesirable.

Agree with @sdaftuar; IsIBD() is called in a few performance-critical places and at the very least this change should be benchmarked before merge. I'm Concept NACK if this change is essentially just for refactoring's sake; if there's some overarching benefit it would be nice to hear that.

@jonatack
Copy link
Member Author

jonatack commented Jan 31, 2022

@jamesob I agree; potentially improving performance by not needlessly retaking the cs_main lock is the only point here.

@jonatack jonatack changed the title validation, refactor: remove cs_main lock from CChainState::IsInitialBlockDownload() validation: remove cs_main lock from CChainState::IsInitialBlockDownload() Jan 31, 2022
@DrahtBot
Copy link
Contributor

DrahtBot commented Jan 31, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #24178 (p2p: Respond to getheaders if we have sufficient chainwork by sdaftuar)
  • #24171 (p2p: Sync chain more readily from inbound peers during IBD by sdaftuar)
  • #24008 (assumeutxo: net_processing changes by jamesob)
  • #22564 (refactor: Move mutable globals cleared in ::UnloadBlockIndex to BlockManager by dongcarl)
  • #21160 (net/net processing: Move tx inventory into net_processing by jnewbery)
  • #20827 (During IBD, prune as much as possible until we get close to where we will eventually keep blocks by luke-jr)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@sdaftuar
Copy link
Member

potentially improving performance by not needlessly retaking the cs_main lock is the only point here

AFAIU lock acquisition is essentially free if it's already held, so I would be surprised if there's a performance gain from avoiding re-acquisition.

@jonatack
Copy link
Member Author

Looking ahead at the assumeutxo work in the pipeline, IsIBD() will see further callers already holding cs_main. If the assumption holds that a thread safety annotation is preferable to retaking a lock, we might have our cake and eat it too: the majority can call the core IsIBD() with an annotation, and the seven callers sans lock can go through a wrapper with optimization and lock.

maflcko
maflcko previously requested changes Feb 1, 2022
@maflcko
Copy link
Member

maflcko commented Feb 1, 2022

This may also have a silent merge conflict with #22053?

@jonatack jonatack force-pushed the remove-cs_main-lock-from-IsInitialBlockDownload branch from 5159d2c to b1b3350 Compare February 1, 2022 21:26
@jonatack
Copy link
Member Author

jonatack commented Feb 1, 2022

Pushed a different approach along the lines of #24220 (comment). It does not increase the scope of cs_main and maintains the optimization, while avoiding reacquiring the mutex during IBD for the majority of callers that already hold it.

This may also have a silent merge conflict with #22053?

With the latest push, if MaybeSendAddr no longer holds cs_main it should call IsIBD, otherwise clang provides a thread safety warning.

@jonatack jonatack changed the title validation: remove cs_main lock from CChainState::IsInitialBlockDownload() validation: don't re-acquire cs_main during IBD in CChainState::IsInitialBlockDownload() Feb 1, 2022
@jonatack jonatack changed the title validation: don't re-acquire cs_main during IBD in CChainState::IsInitialBlockDownload() validation: don't re-acquire cs_main after IBD in CChainState::IsInitialBlockDownload() Feb 1, 2022
@jonatack jonatack changed the title validation: don't re-acquire cs_main after IBD in CChainState::IsInitialBlockDownload() validation: don't re-acquire cs_main during IBD in CChainState::IsInitialBlockDownload() Feb 1, 2022
@DrahtBot DrahtBot mentioned this pull request Feb 2, 2022
18 tasks
@jonatack jonatack force-pushed the remove-cs_main-lock-from-IsInitialBlockDownload branch from b1b3350 to a22c867 Compare February 4, 2022 15:15
@maflcko
Copy link
Member

maflcko commented Feb 4, 2022

to reduce the use of cs_main as a recursive mutex.

I think this is a goal that is almost impossible to achieve with a mutex visible in the global scope/namespace. And if someone did it, it would be brittle and easily lead to UB.

See also how for a far simpler and private mutex (m_chainstate_mutex), it took two attempts to get right: #24235.

Personally, I think a better way would be to no longer hold cs_main for non-validation uses and then make cs_main private to chainman.

@jonatack
Copy link
Member Author

jonatack commented Feb 4, 2022

Yes. This change makes explicit which callers hold cs_main and which do not, now and going forward. Edit: updated the pull description.

…ng IBD

as most of its callers already hold the cs_main lock;
have these callers invoke non-locking IsIBD() instead.

Retains the post-IBD optimization latch for the callers that
do not hold the lock.

Makes explicit which callers hold cs_main and which do not.
Ensures that callers now and going forward invoke the correct function.
@jonatack jonatack force-pushed the remove-cs_main-lock-from-IsInitialBlockDownload branch from a22c867 to 601f90e Compare February 7, 2022 12:23
@jonatack
Copy link
Member Author

jonatack commented Feb 7, 2022

Updated with @luke-jr and @PastaPastaPasta feedback (thanks!)

@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft".

@DrahtBot
Copy link
Contributor

There hasn't been much activity lately and the patch still needs rebase. What is the status here?
  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

@maflcko
Copy link
Member

maflcko commented Jul 25, 2022

Closing for now due to inactivity. Let us know if this should be reopened.

@maflcko maflcko closed this Jul 25, 2022
@bitcoin bitcoin locked and limited conversation to collaborators Jul 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants