From c248caea7e6dff958165dacff037319eb0b4a686 Mon Sep 17 00:00:00 2001
From: HashEngineering
Date: Tue, 22 Jul 2025 00:08:25 -0700
Subject: [PATCH] fix: change Wallet.lock to ReentrantReadWriteLock
---
.../java/org/bitcoinj/utils/Threading.java | 8 +
.../main/java/org/bitcoinj/wallet/Wallet.java | 344 +++++++++---------
.../java/org/bitcoinj/wallet/WalletEx.java | 36 +-
3 files changed, 198 insertions(+), 190 deletions(-)
diff --git a/core/src/main/java/org/bitcoinj/utils/Threading.java b/core/src/main/java/org/bitcoinj/utils/Threading.java
index 9e60575da..6f8bcf2cd 100644
--- a/core/src/main/java/org/bitcoinj/utils/Threading.java
+++ b/core/src/main/java/org/bitcoinj/utils/Threading.java
@@ -31,6 +31,7 @@
import java.lang.management.ThreadMXBean;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Various threading related utilities. Provides a wrapper around explicit lock creation that lets you control whether
@@ -192,6 +193,13 @@ public static CycleDetectingLockFactory.Policy getPolicy() {
return policy;
}
+ public static ReentrantReadWriteLock readWriteLock(String name) {
+ if (Utils.isAndroidRuntime() && useDefaultAndroidPolicy)
+ return new ReentrantReadWriteLock(true);
+ else
+ return factory.newReentrantReadWriteLock(name);
+ }
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Generic worker pool.
diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java
index c435b0baa..a74943bd8 100644
--- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java
+++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java
@@ -133,7 +133,7 @@ public class Wallet extends BaseTaggableObject
// Ordering: lock > keyChainGroupLock. KeyChainGroup is protected separately to allow fast querying of current receive address
// even if the wallet itself is busy e.g. saving or processing a big reorg. Useful for reducing UI latency.
- protected final ReentrantLock lock = Threading.lock("wallet");
+ protected final ReentrantReadWriteLock lock = Threading.readWriteLock("wallet");
protected final ReentrantLock keyChainGroupLock = Threading.lock("wallet-keychaingroup");
// The various pools below give quick access to wallet-relevant transactions by the state they're in:
@@ -513,17 +513,17 @@ public void onConfidenceChanged(TransactionConfidence confidence, TransactionCon
// to us to listen for that. Other types of confidence changes (type, etc) are triggered by us,
// so we'll queue up a wallet change event in other parts of the code.
if (reason == ChangeReason.SEEN_PEERS) {
- lock.lock();
+ lock.writeLock().lock();
try {
checkBalanceFuturesLocked(null);
Transaction tx = getTransaction(confidence.getTransactionHash());
queueOnTransactionConfidenceChanged(tx);
maybeQueueOnWalletChanged();
} finally {
- lock.unlock();
+ lock.writeLock().unlock();
}
} else if(reason == ChangeReason.IX_TYPE || reason == ChangeReason.REJECT) {
- lock.lock();
+ lock.writeLock().lock();
try {
Transaction tx = getTransaction(confidence.getTransactionHash());
queueOnTransactionConfidenceChanged(tx);
@@ -531,7 +531,7 @@ public void onConfidenceChanged(TransactionConfidence confidence, TransactionCon
//save the wallet when an InstantSend transaction is locked
saveLater();
} finally {
- lock.unlock();;
+ lock.writeLock().unlock();
}
}
}
@@ -575,23 +575,23 @@ public DeterministicKeyChain getActiveKeyChain() {
* will be thrown
*/
public final void addTransactionSigner(TransactionSigner signer) {
- lock.lock();
+ lock.writeLock().lock();
try {
if (signer.isReady())
signers.add(signer);
else
throw new IllegalStateException("Signer instance is not ready to be added into Wallet: " + signer.getClass());
} finally {
- lock.unlock();
+ lock.writeLock().unlock();
}
}
public List getTransactionSigners() {
- lock.lock();
+ lock.readLock().lock();
try {
return ImmutableList.copyOf(signers);
} finally {
- lock.unlock();
+ lock.readLock().unlock();
}
}
@@ -1125,7 +1125,7 @@ public boolean removeWatchedAddresses(final List addresses) {
* @return true if successful
*/
public boolean removeWatchedScripts(final List