Skip to content

Conversation

@HashEngineering
Copy link
Collaborator

@HashEngineering HashEngineering commented Jul 22, 2025

Issue being fixed or feature implemented

What was done?

Replace ReentrantLock with ReentrantReadWriteLock in Wallet for the main lock.

How Has This Been Tested?

Breaking Changes

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • Refactor
    • Improved internal synchronization in the wallet by transitioning from exclusive locks to read-write locks, allowing for better concurrency and performance during read operations.
    • Updated internal mechanisms to use shared read locks for read-only wallet operations, reducing contention and improving efficiency.
    • No changes to public APIs or user-facing functionality.

@HashEngineering HashEngineering self-assigned this Jul 22, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 22, 2025

Walkthrough

The changes introduce a new static method in the Threading utility for creating named ReentrantReadWriteLocks. The Wallet class switches its internal synchronization from a simple ReentrantLock to a ReentrantReadWriteLock, updating all usages accordingly. WalletEx updates its read-only methods to use the shared read lock instead of the exclusive write lock.

Changes

File(s) Change Summary
core/src/main/java/org/bitcoinj/utils/Threading.java Added readWriteLock(String name) static method to return a named ReentrantReadWriteLock; updated imports.
core/src/main/java/org/bitcoinj/wallet/Wallet.java Replaced ReentrantLock with ReentrantReadWriteLock; updated all lock usage to use read/write locks as appropriate.
core/src/main/java/org/bitcoinj/wallet/WalletEx.java Updated read-only methods to use readLock() instead of write lock for synchronization.

Estimated code review effort

3 (~45 minutes)

Poem

A rabbit hops through code so tight,
Locks now let readers share the light.
Wallets guard their coins with care,
Read and write, a balanced pair.
With named locks, threads dance with glee—
Concurrency, as safe as can be!
🐇🔒

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.38.6)
core/src/main/java/org/bitcoinj/wallet/Wallet.java
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cf811a and c248cae.

📒 Files selected for processing (3)
  • core/src/main/java/org/bitcoinj/utils/Threading.java (2 hunks)
  • core/src/main/java/org/bitcoinj/wallet/Wallet.java (111 hunks)
  • core/src/main/java/org/bitcoinj/wallet/WalletEx.java (16 hunks)
🧬 Code Graph Analysis (1)
core/src/main/java/org/bitcoinj/utils/Threading.java (1)
core/src/main/java/org/bitcoinj/core/Utils.java (1)
  • Utils (64-862)
🧰 Additional context used
🧬 Code Graph Analysis (1)
core/src/main/java/org/bitcoinj/utils/Threading.java (1)
core/src/main/java/org/bitcoinj/core/Utils.java (1)
  • Utils (64-862)
🔇 Additional comments (13)
core/src/main/java/org/bitcoinj/utils/Threading.java (2)

34-34: LGTM - Import addition is correct.

The import for ReentrantReadWriteLock is properly added to support the new readWriteLock method.


196-201: LGTM - Consistent implementation pattern.

The readWriteLock method correctly follows the same pattern as the existing lock method:

  • Uses the same Android runtime detection and policy checks
  • Returns a fair ReentrantReadWriteLock for Android environments
  • Delegates to the cycle-detecting factory for other environments

This ensures consistent behavior and proper cycle detection capabilities.

core/src/main/java/org/bitcoinj/wallet/WalletEx.java (9)

202-202: LGTM - Correct use of read lock for balance calculation.

The getBalance method is a read-only operation that calculates wallet balance without modifying state. Using readLock() is appropriate and allows concurrent balance queries from multiple threads.

Also applies to: 237-237


285-285: LGTM - Appropriate read lock for spent status check.

The isSpent method only reads transaction state and doesn't modify the wallet. Using the read lock is correct and improves concurrency.

Also applies to: 295-295


318-318: LGTM - Read lock correctly used for ownership check.

The isMine(TransactionInput input) method only reads transaction data to determine ownership. The read lock usage is appropriate for this read-only operation.

Also applies to: 327-327


333-333: LGTM - Read lock appropriate for CoinJoin rounds calculation.

The getRealOutpointCoinJoinRounds method performs complex read-only analysis of transaction chains. Using the read lock allows multiple threads to perform this calculation concurrently without blocking each other.

Also applies to: 422-422


434-434: LGTM - Read lock suitable for fully mixed status check.

The isFullyMixed method only reads transaction data to determine mixing status. The read lock usage enables concurrent queries while maintaining thread safety.

Also applies to: 460-460


489-489: LGTM - Read lock appropriate for coin selection.

The selectCoinsGroupedByAddresses method performs read-only analysis of available coins and grouping. Using the read lock allows concurrent coin selection operations.

Also applies to: 603-603


693-693: LGTM - Read lock correct for destination usage check.

The isUsedDestination method only reads destination data without modification. The read lock usage is appropriate for this read-only query.

Also applies to: 697-697


731-731: LGTM - Read lock suitable for available coins enumeration.

The availableCoins method performs read-only traversal of unspent transactions to build a list of available coins. Using the read lock allows concurrent access to this information.

Also applies to: 824-824


888-888: LGTM - Read lock appropriate for denominated amount selection.

The selectDenominatedAmounts method only reads coin data to select denominated amounts. The read lock usage enables concurrent selection operations while maintaining thread safety.

Also applies to: 911-911

core/src/main/java/org/bitcoinj/wallet/Wallet.java (2)

136-136: Good use of read-write lock for improved concurrency

Replacing ReentrantLock with ReentrantReadWriteLock is an excellent optimization that allows multiple threads to read wallet state concurrently while maintaining exclusive access for writes.


4409-4409: Good deadlock prevention practice

Excellent defensive programming - ensuring the write lock is not held when calling the external broadcaster prevents potential deadlocks with external components.

*/
private Set<Transaction> findDoubleSpendsAgainst(Transaction tx, Map<Sha256Hash, Transaction> candidates) {
checkState(lock.isHeldByCurrentThread());
// checkState(lock.writeLock().isHeldByCurrentThread());
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify the commented lock assertion

The checkState for write lock verification is commented out. This method findDoubleSpendsAgainst appears to be read-only (just finding transactions), so it might be called from contexts holding either read or write locks. However, this should be explicitly documented or the assertion should be updated to check for either lock type being held.

-        // checkState(lock.writeLock().isHeldByCurrentThread());
+        // This method can be called with either read or write lock held
+        checkState(lock.readLock().isHeldByCurrentThread() || lock.writeLock().isHeldByCurrentThread());

🏁 Script executed:

#!/bin/bash
# Display the findDoubleSpendsAgainst method to verify its lock requirements
rg -n -C5 "findDoubleSpendsAgainst" core/src/main/java/org/bitcoinj/wallet/Wallet.java

Length of output: 4099


Enforce read or write lock in findDoubleSpendsAgainst
The helper findDoubleSpendsAgainst is invoked under a read lock (and occasionally under a write lock). Restore the assertion to require that either lock is held, rather than only the write lock.

• File: core/src/main/java/org/bitcoinj/wallet/Wallet.java
• Line: 2234

-        // checkState(lock.writeLock().isHeldByCurrentThread());
+        // Ensure we hold the wallet lock (read or write) when scanning for double spends.
+        checkState(
+            lock.getReadHoldCount() > 0 ||
+            lock.writeLock().isHeldByCurrentThread(),
+            "Must hold read or write lock when calling findDoubleSpendsAgainst"
+        );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// checkState(lock.writeLock().isHeldByCurrentThread());
// Ensure we hold the wallet lock (read or write) when scanning for double spends.
checkState(
lock.getReadHoldCount() > 0 ||
lock.writeLock().isHeldByCurrentThread(),
"Must hold read or write lock when calling findDoubleSpendsAgainst"
);
🤖 Prompt for AI Agents
In core/src/main/java/org/bitcoinj/wallet/Wallet.java at line 2234, the
commented out assertion currently checks only for the write lock being held, but
the method findDoubleSpendsAgainst can be called with either a read or write
lock. Modify the assertion to verify that the current thread holds either the
read lock or the write lock before proceeding, restoring the concurrency safety
check accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants