Skip to content

Conversation

promag
Copy link
Contributor

@promag promag commented Sep 2, 2021

Don't have cs_wallet locked while calling each context.wallet_load_fns. A load handler can always lock cs_wallet if needed.

The lock was added in 1c7e25d to satisfy TSAN. With 44c430f most of the code requiring the lock is in CWallet::AttachChain. A comment is added to warn about wallets_mutex and cs_wallet lock ordering.

@DrahtBot DrahtBot added the Wallet label Sep 2, 2021
@promag promag force-pushed the 2021-09-wallet-load-lock branch 4 times, most recently from 1ba170e to 2c87f99 Compare September 3, 2021 07:47
@DrahtBot
Copy link
Contributor

DrahtBot commented Sep 4, 2021

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

Conflicts

No conflicts as of last run.

@DrahtBot DrahtBot mentioned this pull request Sep 4, 2021
@hebasto
Copy link
Member

hebasto commented Sep 7, 2021

Why this change is safe?

@promag
Copy link
Contributor Author

promag commented Sep 7, 2021

@hebasto what is the concern? The lock can be acquired inside the load handler. This change also avoids recursive locks on cs_wallet.

@ryanofsky
Copy link
Contributor

what is the concern?

This does seem probably safe, but it would save reviewers some effort to know when the lock was added, when it stopped being necessary, and the specific reason why it was necessary whenever it was needed (for example whether it was avoiding deadlocks or asserts by acquiring recursive locks in a consistent order, or whether it was avoiding race condition.) This information could go in the PR description.

One concern might be that one of the wallet_load_fns callbacks could call a wallet method and acquire cs_wallet mutex after context.wallets_mutex, and maybe this would be in inconsistent order.

@promag
Copy link
Contributor Author

promag commented Sep 11, 2021

@ryanofsky

but it would save reviewers some effort to know when the lock was added, when it stopped being necessary

My bad for not writing this, I went thru history before making the change, will redo and update this.

One concern might be that one of the wallet_load_fns callbacks could call a wallet method and acquire cs_wallet mutex after context.wallets_mutex, and maybe this would be in inconsistent order.

Would be bad to lock one cs_wallet after wallets_mutex? In master that already happens but works because cs_wallet is recursive. Couldn't find more cases of cs_wallet -> wallets_mutex lock order.

@promag
Copy link
Contributor Author

promag commented Sep 12, 2021

@ryanofsky see https://github.com/bitcoin/bitcoin/pull/11634/files#r227725517 for when the lock was added and @practicalswift submitted a similar change #14560 but @MarcoFalke rejected it.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Code review ACK 2c87f99. It seems safe to remove this lock because it look like it was only added by accident in #11634 to try to satisfy lock annotations, even though it was never really needed to satisfy lock annotations even according to the thread there https://github.com/bitcoin/bitcoin/pull/11634/files#r227725517. (EDIT: lock was not added by accident, it was needed but was always broader than it needed to be and it became unnecessary after a recursive lock was added in #20773)

⬆️ I think above information would be helpful to add to PR description because otherwise this PR seems like a random change.

After this PR, I think there are no more cases where cs_wallet and wallets_mutex are locked at the same time so there is no required lock ordering between them. But in the future, GUI code could do arbitrary things in its wallet_load_fns handler, and maybe it will call a wallet function and lock cs_wallet in one. In that case the lock order would be wallets_mutex first then cs_wallet second. It may be good to state some intended lock ordering, maybe adding a code comment to WalletContext::wallets_mutex like "It is unsafe to lock this after locking a CWallet::cs_wallet mutex because this could introduce inconsistent lock ordering and cause deadlocks."

One concern might be that one of the wallet_load_fns callbacks could call a wallet method and acquire cs_wallet mutex after context.wallets_mutex, and maybe this would be in inconsistent order.

Would be bad to lock one cs_wallet after wallets_mutex?

If there is any thread locking wallets_mutex after cs_wallet then it would be bad, but if not, then it's fine.

In master that already happens but works because cs_wallet is recursive. Couldn't find more cases of cs_wallet -> wallets_mutex lock order.

I don't think this matters because recursive locks are no-ops. It a thread locks mutex A, then locks mutex B, then recursively locks mutex A again, then the recursive lock isn't doing anything, and the lock order is still A before B.

@promag
Copy link
Contributor Author

promag commented Sep 24, 2021

@ryanofsky thanks! You might want to see bitcoin-core/gui@c09fd3a, it needs this change otherwise it deadlocks.

@ryanofsky
Copy link
Contributor

@ryanofsky thanks! You might want to see bitcoin-core/gui@c09fd3a, it needs this change otherwise it deadlocks.

Thanks! That also looks like a good change. Is there a PR or issue this is all a part of?

@promag
Copy link
Contributor Author

promag commented Nov 8, 2021

@ryanofsky yes, bitcoin-core/gui#417

Edit: I guess I have to rebase.

@promag promag force-pushed the 2021-09-wallet-load-lock branch from 2c87f99 to 7850d33 Compare November 8, 2021 21:54
@promag
Copy link
Contributor Author

promag commented Nov 8, 2021

Rebased due to trivial conflict with #23123.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Code review ACK 7850d33 with caveats from #22868 (review) that I'm not 100% sure this won't cause deadlocks now or in the future, but do think this seems safe and is probably a good simplification. Also I think the PR description is lacking essential information like why it is safe to remove the lock and how long it's been unnecessary.

Copy link
Contributor

@meshcollider meshcollider left a comment

Choose a reason for hiding this comment

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

Looks correct to me, utACK 7850d33

Agree with Russ though, definitely think the PR description needs updating.

@promag promag force-pushed the 2021-09-wallet-load-lock branch from 7850d33 to c34d934 Compare November 22, 2021 09:32
@promag
Copy link
Contributor Author

promag commented Nov 22, 2021

But in the future, GUI code could do arbitrary things in its wallet_load_fns handler, and maybe it will call a wallet function and lock cs_wallet in one. In that case the lock order would be wallets_mutex first then cs_wallet second

This is already the case and it is where the GUI models are filled.

@meshcollider @ryanofsky updated OP, added wallets_mutex comment as suggested.

Also rebased.

Co-authored-by: Russell Yanofsky <russ@yanofsky.org>
@promag promag force-pushed the 2021-09-wallet-load-lock branch from c34d934 to f13a22a Compare November 22, 2021 09:43
Copy link
Contributor

@meshcollider meshcollider left a comment

Choose a reason for hiding this comment

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

re-utACK f13a22a

@ryanofsky
Copy link
Contributor

PR description doesn't seem totally accurate, from #22868 (comment)

Don't have cs_wallet locked while calling each context.wallet_load_fns. A load handler can always lock cs_wallet if needed.

The lock was added in 1c7e25d to satisfy TSAN. With 44c430f most of the code requiring the lock is in CWallet::AttachChain. A comment is added to warn about wallets_mutex and cs_wallet lock ordering.

If my understanding of https://github.com/bitcoin/bitcoin/pull/11634/files#r227725517 is correct, then the lock was not added to satsify TSAN but was added to satisfy clang annotations. I think you could summarize this by saying that this LOCK(cs_wallet) was correctly added in #11634, but it became no longer unnecessary after #20773 when code requiring the lock was moved to the new AttachChain method.

Also it now seems like the motivation for this change is not just removing an unnecessary lock, but reversing the 1. cs_wallet 2. wallets_mutex lock order so it can now be 1. wallets_mutex 2. cs_wallet, because upcoming GUI PR bitcoin-core/gui#417 needs this

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Code review ACK f13a22a. Only change since last review is adding a lock order comment

Copy link
Member

@jonatack jonatack left a comment

Choose a reason for hiding this comment

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

ACK f13a22a

@@ -34,6 +34,8 @@ using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wall
struct WalletContext {
interfaces::Chain* chain{nullptr};
ArgsManager* args{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
// It is unsafe to lock this after locking a CWallet::cs_wallet mutex because
// this could introduce inconsistent lock ordering and cause deadlocks.
Copy link
Member

@jonatack jonatack Nov 24, 2021

Choose a reason for hiding this comment

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

Haven't tried, but would there be any point in using EXCLUDES / LOCKS_EXCLUDED (or REQUIRES(!mutex) with -Wthread-safety-negative) annotations to enforce this?

@meshcollider meshcollider merged commit 200d97f into bitcoin:master Nov 27, 2021
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Nov 27, 2021
f13a22a wallet: Call load handlers without cs_wallet locked (João Barbosa)

Pull request description:

  Don't have `cs_wallet` locked while calling each `context.wallet_load_fns`. A load handler can always lock `cs_wallet` if needed.

  The lock was added in 1c7e25d to satisfy TSAN. With 44c430f most of the code requiring the lock is in `CWallet::AttachChain`. A comment is added to warn about wallets_mutex and cs_wallet lock ordering.

ACKs for top commit:
  meshcollider:
    re-utACK f13a22a
  ryanofsky:
    Code review ACK f13a22a. Only change since last review is adding a lock order comment
  jonatack:
    ACK f13a22a

Tree-SHA512: d51976c3aae4bebc2d1997c88edff712d21fc5523801f5614062a10f826e164579973aeb1981bb1cbc243ecff6af3250362f544c02a79e5d135cbbca1704be62
@promag promag deleted the 2021-09-wallet-load-lock branch February 23, 2022 00:49
Copy link

@Gemk83 Gemk83 left a comment

Choose a reason for hiding this comment

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

Ok

@bitcoin bitcoin locked and limited conversation to collaborators Feb 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants