Wallet Locks

Grepping the src/wallet directory for locks, conventionally of the form cs_*, yields ~500 matches. For comparison the entire remainder of the codebase excluding src/wallet/* yields almost 1000 matches. Many of these matches are asserts and declarations, however this still illustrates that the wallet code is highly reliant on locks to perform atomic operations with respect to the current chain state.

The cs_wallet lock

In order to not block the rest of the program during wallet operations, each CWallet has its own recursive mutex cs_wallet:

There is currently an issue tracking replacement of Recursive Mutexes with Mutexes, to make locking logic easier to follow in the codebase.
src/wallet/wallet.h
/*
 * Main wallet lock.
 * This lock protects all the fields added by CWallet.
 */
mutable RecursiveMutex cs_wallet;

Most wallet operations whether reading or writing data require the use of the lock so that atomicity can be guaranteed. Some examples of wallet operations requiring the lock include:

  1. Creating transactions

  2. Signing transactions

  3. Broadcasting/committing transactions

  4. Abandoning transactions

  5. Bumping transaction (fees)

  6. Checking IsMine

  7. Creating new addresses

  8. Calculating balances

  9. Creating new wallets

  10. Importing new {priv|pub}keys/addresses

  11. Importing/dumping wallets

In addition to these higher level functions, most of CWallet's private member functions also require a hold on cs_wallet.

Other wallet locks

  1. src/wallet/bdb.cpp, which is responsible for managing BDB wallet databases on disk, has its own mutex cs_db.

  2. If external signers have been enabled (via ./configure --enable-external-signer) then they too have their own mutex cs_desc_man which is acquired when descriptors are being setup.

  3. BlockUntilSyncedToCurrentChain() has a unique lock exclude placed on it to prevent the caller from holding cs_main during its execution, and therefore prevent a possible deadlock:

    src/wallet/wallet.h
    /**
     * Blocks until the wallet state is up-to-date to /at least/ the current
     * chain at the time this function is entered
     * Obviously holding cs_main/cs_wallet when going into this call may cause
     * deadlock
     */
    void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(::cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet);