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