handle connection change on dedicated thread, other thread improvements

This commit is contained in:
woodser 2023-12-18 08:01:07 -05:00
parent b1f8411641
commit f162cad439
11 changed files with 121 additions and 72 deletions

View File

@ -263,6 +263,7 @@ public final class XmrConnectionService {
public void verifyConnection() { public void verifyConnection() {
if (daemon == null) throw new RuntimeException("No connection to Monero node"); if (daemon == null) throw new RuntimeException("No connection to Monero node");
if (!Boolean.TRUE.equals(isConnected())) throw new RuntimeException("No connection to Monero node");
if (!isSyncedWithinTolerance()) throw new RuntimeException("Monero node is not synced"); if (!isSyncedWithinTolerance()) throw new RuntimeException("Monero node is not synced");
} }
@ -493,10 +494,11 @@ public final class XmrConnectionService {
}, getDefaultRefreshPeriodMs() * 2 / 1000); }, getDefaultRefreshPeriodMs() * 2 / 1000);
} }
// notify final connection
isInitialized = true; isInitialized = true;
onConnectionChanged(connectionManager.getConnection());
} }
// notify initial connection
onConnectionChanged(connectionManager.getConnection());
} }
private void maybeStartLocalNode() { private void maybeStartLocalNode() {
@ -541,7 +543,7 @@ public final class XmrConnectionService {
// notify listeners in parallel // notify listeners in parallel
synchronized (listenerLock) { synchronized (listenerLock) {
for (MoneroConnectionManagerListener listener : listeners) { for (MoneroConnectionManagerListener listener : listeners) {
new Thread(() -> listener.onConnectionChanged(currentConnection)).start(); HavenoUtils.submitToPool(() -> listener.onConnectionChanged(currentConnection));
} }
} }
} }

View File

@ -34,8 +34,6 @@ import haveno.network.p2p.BootstrapListener;
import haveno.network.p2p.P2PService; import haveno.network.p2p.P2PService;
import haveno.network.p2p.storage.HashMapChangedListener; import haveno.network.p2p.storage.HashMapChangedListener;
import haveno.network.p2p.storage.payload.ProtectedStorageEntry; import haveno.network.p2p.storage.payload.ProtectedStorageEntry;
import monero.common.MoneroConnectionManagerListener;
import monero.common.MoneroRpcConnection;
import monero.daemon.model.MoneroKeyImageSpentStatus; import monero.daemon.model.MoneroKeyImageSpentStatus;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -94,13 +92,10 @@ public class OfferBookService {
jsonFileManager = new JsonFileManager(storageDir); jsonFileManager = new JsonFileManager(storageDir);
// listen for connection changes to monerod // listen for connection changes to monerod
xmrConnectionService.addConnectionListener(new MoneroConnectionManagerListener() { xmrConnectionService.addConnectionListener((connection) -> {
@Override
public void onConnectionChanged(MoneroRpcConnection connection) {
maybeInitializeKeyImagePoller(); maybeInitializeKeyImagePoller();
keyImagePoller.setDaemon(xmrConnectionService.getDaemon()); keyImagePoller.setDaemon(xmrConnectionService.getDaemon());
keyImagePoller.setRefreshPeriodMs(getKeyImageRefreshPeriodMs()); keyImagePoller.setRefreshPeriodMs(getKeyImageRefreshPeriodMs());
}
}); });
// listen for offers // listen for offers

View File

@ -75,8 +75,6 @@ import haveno.network.p2p.peers.PeerManager;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import lombok.Getter; import lombok.Getter;
import monero.common.MoneroConnectionManagerListener;
import monero.common.MoneroRpcConnection;
import monero.daemon.model.MoneroKeyImageSpentStatus; import monero.daemon.model.MoneroKeyImageSpentStatus;
import monero.daemon.model.MoneroTx; import monero.daemon.model.MoneroTx;
import monero.wallet.model.MoneroIncomingTransfer; import monero.wallet.model.MoneroIncomingTransfer;
@ -208,12 +206,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
this.signedOfferPersistenceManager.initialize(signedOffers, "SignedOffers", PersistenceManager.Source.PRIVATE); // arbitrator stores reserve tx for signed offers this.signedOfferPersistenceManager.initialize(signedOffers, "SignedOffers", PersistenceManager.Source.PRIVATE); // arbitrator stores reserve tx for signed offers
// listen for connection changes to monerod // listen for connection changes to monerod
xmrConnectionService.addConnectionListener(new MoneroConnectionManagerListener() { xmrConnectionService.addConnectionListener((connection) -> maybeInitializeKeyImagePoller());
@Override
public void onConnectionChanged(MoneroRpcConnection connection) {
maybeInitializeKeyImagePoller();
}
});
// close open offer if reserved funds spent // close open offer if reserved funds spent
offerBookService.addOfferBookChangedListener(new OfferBookChangedListener() { offerBookService.addOfferBookChangedListener(new OfferBookChangedListener() {
@ -308,7 +301,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
} }
public void shutDown(@Nullable Runnable completeHandler) { public void shutDown(@Nullable Runnable completeHandler) {
HavenoUtils.shutDownThreadId(THREAD_ID); HavenoUtils.removeThreadId(THREAD_ID);
stopped = true; stopped = true;
p2PService.getPeerManager().removeListener(this); p2PService.getPeerManager().removeListener(this);
p2PService.removeDecryptedDirectMessageListener(this); p2PService.removeDecryptedDirectMessageListener(this);

View File

@ -235,7 +235,7 @@ public class PriceFeedService {
if (baseUrlOfRespondingProvider == null) { if (baseUrlOfRespondingProvider == null) {
final String oldBaseUrl = priceProvider.getBaseUrl(); final String oldBaseUrl = priceProvider.getBaseUrl();
setNewPriceProvider(); setNewPriceProvider();
log.warn("We did not received a response from provider {}. " + log.warn("We did not receive a response from provider {}. " +
"We select the new provider {} and use that for a new request.", oldBaseUrl, priceProvider.getBaseUrl()); "We select the new provider {} and use that for a new request.", oldBaseUrl, priceProvider.getBaseUrl());
} }
request(true); request(true);

View File

@ -398,7 +398,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
if (!expectedSellerAmount.equals(actualSellerAmount)) throw new RuntimeException("Unexpected seller payout: " + expectedSellerAmount + " vs " + actualSellerAmount); if (!expectedSellerAmount.equals(actualSellerAmount)) throw new RuntimeException("Unexpected seller payout: " + expectedSellerAmount + " vs " + actualSellerAmount);
// check wallet's daemon connection // check wallet's daemon connection
trade.checkDaemonConnection(); trade.checkAndVerifyDaemonConnection();
// determine if we already signed dispute payout tx // determine if we already signed dispute payout tx
// TODO: better way, such as by saving signed dispute payout tx hex in designated field instead of shared payoutTxHex field? // TODO: better way, such as by saving signed dispute payout tx hex in designated field instead of shared payoutTxHex field?

View File

@ -45,6 +45,7 @@ import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -474,7 +475,13 @@ public class HavenoUtils {
} }
public static Future<?> submitToPool(Runnable task) { public static Future<?> submitToPool(Runnable task) {
return POOL.submit(task); return submitToPool(Arrays.asList(task)).get(0);
}
public static List<Future<?>> submitToPool(List<Runnable> tasks) {
List<Future<?>> futures = new ArrayList<>();
for (Runnable task : tasks) futures.add(POOL.submit(task));
return futures;
} }
public static Future<?> submitToSharedThread(Runnable task) { public static Future<?> submitToSharedThread(Runnable task) {
@ -488,7 +495,17 @@ public class HavenoUtils {
} }
} }
public static void shutDownThreadId(String threadId) { public static Future<?> awaitThread(Runnable task, String threadId) {
Future<?> future = submitToThread(task, threadId);
try {
future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
return future;
}
public static void removeThreadId(String threadId) {
synchronized (POOLS) { synchronized (POOLS) {
if (POOLS.containsKey(threadId)) { if (POOLS.containsKey(threadId)) {
POOLS.get(threadId).shutdown(); POOLS.get(threadId).shutdown();
@ -497,7 +514,11 @@ public class HavenoUtils {
} }
} }
// TODO: update monero-java and replace with GenUtils.awaitTasks() // TODO: these are unused; remove? use monero-java awaitTasks() when updated
public static Future<?> awaitTask(Runnable task) {
return awaitTasks(Arrays.asList(task)).get(0);
}
public static List<Future<?>> awaitTasks(Collection<Runnable> tasks) { public static List<Future<?>> awaitTasks(Collection<Runnable> tasks) {
return awaitTasks(tasks, tasks.size()); return awaitTasks(tasks, tasks.size());

View File

@ -102,6 +102,7 @@ import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -376,6 +377,8 @@ public abstract class Trade implements Tradable, Model {
// Immutable // Immutable
@Getter @Getter
transient final private XmrWalletService xmrWalletService; transient final private XmrWalletService xmrWalletService;
@Getter
transient final private XmrConnectionService xmrConnectionService;
transient final private DoubleProperty initProgressProperty = new SimpleDoubleProperty(0.0); transient final private DoubleProperty initProgressProperty = new SimpleDoubleProperty(0.0);
transient final private ObjectProperty<State> stateProperty = new SimpleObjectProperty<>(state); transient final private ObjectProperty<State> stateProperty = new SimpleObjectProperty<>(state);
@ -476,6 +479,7 @@ public abstract class Trade implements Tradable, Model {
this.takerFee = takerFee.longValueExact(); this.takerFee = takerFee.longValueExact();
this.price = tradePrice; this.price = tradePrice;
this.xmrWalletService = xmrWalletService; this.xmrWalletService = xmrWalletService;
this.xmrConnectionService = xmrWalletService.getConnectionService();
this.processModel = processModel; this.processModel = processModel;
this.uid = uid; this.uid = uid;
this.takeOfferDate = new Date().getTime(); this.takeOfferDate = new Date().getTime();
@ -588,9 +592,11 @@ public abstract class Trade implements Tradable, Model {
getArbitrator().setPubKeyRing(arbitrator.getPubKeyRing()); getArbitrator().setPubKeyRing(arbitrator.getPubKeyRing());
}); });
// handle daemon changes with max parallelization // handle connection change on dedicated thread
xmrWalletService.getConnectionService().addConnectionListener(newConnection -> { xmrConnectionService.addConnectionListener(connection -> {
HavenoUtils.submitToPool(() -> onConnectionChanged(newConnection)); HavenoUtils.submitToPool(() -> {
HavenoUtils.submitToThread(() -> onConnectionChanged(connection), getConnectionChangedThreadId());
});
}); });
// check if done // check if done
@ -644,7 +650,7 @@ public abstract class Trade implements Tradable, Model {
new Thread(() -> { new Thread(() -> {
GenUtils.waitFor(1000); GenUtils.waitFor(1000);
if (isShutDownStarted) return; if (isShutDownStarted) return;
if (Boolean.TRUE.equals(xmrWalletService.getConnectionService().isConnected())) xmrWalletService.syncWallet(xmrWalletService.getWallet()); if (Boolean.TRUE.equals(xmrConnectionService.isConnected())) xmrWalletService.syncWallet(xmrWalletService.getWallet());
}).start(); }).start();
// complete disputed trade // complete disputed trade
@ -762,21 +768,33 @@ public abstract class Trade implements Tradable, Model {
return MONERO_TRADE_WALLET_PREFIX + getId(); return MONERO_TRADE_WALLET_PREFIX + getId();
} }
public void checkDaemonConnection() { public void checkAndVerifyDaemonConnection() {
XmrConnectionService xmrConnectionService = xmrWalletService.getConnectionService();
// check connection which might update
xmrConnectionService.checkConnection(); xmrConnectionService.checkConnection();
xmrConnectionService.verifyConnection(); xmrConnectionService.verifyConnection();
if (!getWallet().isConnectedToDaemon()) throw new RuntimeException("Trade wallet is not connected to a Monero node");
// check wallet connection on same thread as connection change
CountDownLatch latch = new CountDownLatch(1);
HavenoUtils.submitToPool((() -> {
HavenoUtils.submitToThread(() -> {
if (!isWalletConnectedToDaemon()) throw new RuntimeException("Trade wallet is not connected to a Monero node"); // wallet connection is updated on trade thread
latch.countDown();
}, getConnectionChangedThreadId());
}));
HavenoUtils.awaitLatch(latch); // TODO: better way?
} }
public boolean isWalletConnected() { public boolean isWalletConnectedToDaemon() {
synchronized (walletLock) {
try { try {
checkDaemonConnection(); if (wallet == null) return false;
return true; return wallet.isConnectedToDaemon();
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
} }
}
public boolean isIdling() { public boolean isIdling() {
return this instanceof ArbitratorTrade && isDepositsConfirmed() && walletExists() && syncNormalStartTimeMs == null; // arbitrator idles trade after deposits confirm unless overriden return this instanceof ArbitratorTrade && isDepositsConfirmed() && walletExists() && syncNormalStartTimeMs == null; // arbitrator idles trade after deposits confirm unless overriden
@ -790,7 +808,7 @@ public abstract class Trade implements Tradable, Model {
syncNormalStartTimeMs = System.currentTimeMillis(); syncNormalStartTimeMs = System.currentTimeMillis();
// override wallet refresh period // override wallet refresh period
setWalletRefreshPeriod(xmrWalletService.getConnectionService().getRefreshPeriodMs()); setWalletRefreshPeriod(xmrConnectionService.getRefreshPeriodMs());
// reset wallet refresh period after duration // reset wallet refresh period after duration
new Thread(() -> { new Thread(() -> {
@ -934,7 +952,7 @@ public abstract class Trade implements Tradable, Model {
public MoneroTxWallet createPayoutTx() { public MoneroTxWallet createPayoutTx() {
// check connection to monero daemon // check connection to monero daemon
checkDaemonConnection(); checkAndVerifyDaemonConnection();
// check multisig import // check multisig import
if (getWallet().isMultisigImportNeeded()) throw new RuntimeException("Cannot create payout tx because multisig import is needed"); if (getWallet().isMultisigImportNeeded()) throw new RuntimeException("Cannot create payout tx because multisig import is needed");
@ -1022,7 +1040,7 @@ public abstract class Trade implements Tradable, Model {
if (!sellerPayoutDestination.getAmount().equals(expectedSellerPayout)) throw new IllegalArgumentException("Seller destination amount is not deposit amount - trade amount - 1/2 tx costs, " + sellerPayoutDestination.getAmount() + " vs " + expectedSellerPayout); if (!sellerPayoutDestination.getAmount().equals(expectedSellerPayout)) throw new IllegalArgumentException("Seller destination amount is not deposit amount - trade amount - 1/2 tx costs, " + sellerPayoutDestination.getAmount() + " vs " + expectedSellerPayout);
// check wallet connection // check wallet connection
if (sign || publish) checkDaemonConnection(); if (sign || publish) checkAndVerifyDaemonConnection();
// handle tx signing // handle tx signing
if (sign) { if (sign) {
@ -1217,6 +1235,11 @@ public abstract class Trade implements Tradable, Model {
public void onComplete() { public void onComplete() {
} }
public void onRemoved() {
HavenoUtils.removeThreadId(getId());
HavenoUtils.removeThreadId(getConnectionChangedThreadId());
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Abstract // Abstract
@ -1764,7 +1787,7 @@ public abstract class Trade implements Tradable, Model {
*/ */
public long getReprocessDelayInSeconds(int reprocessCount) { public long getReprocessDelayInSeconds(int reprocessCount) {
int retryCycles = 3; // reprocess on next refresh periods for first few attempts (app might auto switch to a good connection) int retryCycles = 3; // reprocess on next refresh periods for first few attempts (app might auto switch to a good connection)
if (reprocessCount < retryCycles) return xmrWalletService.getConnectionService().getRefreshPeriodMs() / 1000; if (reprocessCount < retryCycles) return xmrConnectionService.getRefreshPeriodMs() / 1000;
long delay = 60; long delay = 60;
for (int i = retryCycles; i < reprocessCount; i++) delay *= 2; for (int i = retryCycles; i < reprocessCount; i++) delay *= 2;
return Math.min(MAX_REPROCESS_DELAY_SECONDS, delay); return Math.min(MAX_REPROCESS_DELAY_SECONDS, delay);
@ -1775,6 +1798,10 @@ public abstract class Trade implements Tradable, Model {
// Private // Private
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private String getConnectionChangedThreadId() {
return getId() + ".onConnectionChanged";
}
// lazy initialization // lazy initialization
private ObjectProperty<BigInteger> getAmountProperty() { private ObjectProperty<BigInteger> getAmountProperty() {
if (tradeAmountProperty == null) if (tradeAmountProperty == null)
@ -1812,7 +1839,7 @@ public abstract class Trade implements Tradable, Model {
// sync and reprocess messages on new thread // sync and reprocess messages on new thread
if (isInitialized && connection != null && !Boolean.FALSE.equals(connection.isConnected())) { if (isInitialized && connection != null && !Boolean.FALSE.equals(connection.isConnected())) {
new Thread(() -> initSyncing()).start(); HavenoUtils.submitToPool(() -> initSyncing());
} }
} }
} }
@ -1824,9 +1851,9 @@ public abstract class Trade implements Tradable, Model {
} else { } else {
long startSyncingInMs = ThreadLocalRandom.current().nextLong(0, getWalletRefreshPeriod()); // random time to start syncing long startSyncingInMs = ThreadLocalRandom.current().nextLong(0, getWalletRefreshPeriod()); // random time to start syncing
UserThread.runAfter(() -> { UserThread.runAfter(() -> {
if (!isShutDownStarted) { HavenoUtils.submitToPool(() -> {
initSyncingAux(); if (!isShutDownStarted) initSyncingAux();
} });
}, startSyncingInMs / 1000l); }, startSyncingInMs / 1000l);
} }
} }
@ -1863,7 +1890,7 @@ public abstract class Trade implements Tradable, Model {
if (!wasWalletSynced) { if (!wasWalletSynced) {
wasWalletSynced = true; wasWalletSynced = true;
if (xmrWalletService.isProxyApplied(wasWalletSynced)) { if (xmrWalletService.isProxyApplied(wasWalletSynced)) {
onConnectionChanged(xmrWalletService.getConnectionService().getConnection()); onConnectionChanged(xmrConnectionService.getConnection());
} }
} }
@ -1916,12 +1943,11 @@ public abstract class Trade implements Tradable, Model {
} }
private void pollWallet() { private void pollWallet() {
synchronized (walletLock) {
MoneroWallet wallet = getWallet();
try { try {
synchronized (walletLock) {
// log warning if wallet is too far behind daemon // log warning if wallet is too far behind daemon
MoneroDaemonInfo lastInfo = xmrWalletService.getConnectionService().getLastInfo(); MoneroDaemonInfo lastInfo = xmrConnectionService.getLastInfo();
long walletHeight = wallet.getHeight(); long walletHeight = wallet.getHeight();
if (wasWalletSynced && isDepositsPublished() && !isIdling() && lastInfo != null && walletHeight < lastInfo.getHeight() - 3 && !Config.baseCurrencyNetwork().isTestnet()) { if (wasWalletSynced && isDepositsPublished() && !isIdling() && lastInfo != null && walletHeight < lastInfo.getHeight() - 3 && !Config.baseCurrencyNetwork().isTestnet()) {
log.warn("Wallet is more than 3 blocks behind monerod for {} {}, wallet height={}, monerod height={},", getClass().getSimpleName(), getShortId(), walletHeight, lastInfo.getHeight()); log.warn("Wallet is more than 3 blocks behind monerod for {} {}, wallet height={}, monerod height={},", getClass().getSimpleName(), getShortId(), walletHeight, lastInfo.getHeight());
@ -2000,18 +2026,20 @@ public abstract class Trade implements Tradable, Model {
} }
} }
} }
}
} catch (Exception e) { } catch (Exception e) {
if (!isShutDownStarted && wallet != null && isWalletConnected()) { boolean isWalletConnected = isWalletConnectedToDaemon();
if (!isWalletConnected) xmrConnectionService.checkConnection(); // check connection if wallet is not connected
if (!isShutDownStarted && wallet != null && isWalletConnected) {
e.printStackTrace(); e.printStackTrace();
log.warn("Error polling trade wallet for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getConnectionService().getConnection()); log.warn("Error polling trade wallet for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getConnectionService().getConnection());
} }
} }
} }
}
private long getWalletRefreshPeriod() { private long getWalletRefreshPeriod() {
if (isIdling()) return IDLE_SYNC_PERIOD_MS; if (isIdling()) return IDLE_SYNC_PERIOD_MS;
return xmrWalletService.getConnectionService().getRefreshPeriodMs(); return xmrConnectionService.getRefreshPeriodMs();
} }
private void setStateDepositsPublished() { private void setStateDepositsPublished() {
@ -2082,8 +2110,12 @@ public abstract class Trade implements Tradable, Model {
processing = false; processing = false;
} catch (Exception e) { } catch (Exception e) {
processing = false; processing = false;
boolean isWalletConnected = isWalletConnectedToDaemon();
if (!isWalletConnected) xmrConnectionService.checkConnection(); // check connection if wallet is not connected
if (isInitialized &&!isShutDownStarted && isWalletConnected) {
e.printStackTrace(); e.printStackTrace();
if (isInitialized && !isShutDownStarted && !isWalletConnected()) throw e; log.warn("Error polling idle trade for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getConnectionService().getConnection());
};
} }
}, getId()); }, getId());
} }

View File

@ -1206,7 +1206,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// remove trade // remove trade
tradableList.remove(trade); tradableList.remove(trade);
HavenoUtils.shutDownThreadId(trade.getId()); trade.onRemoved();
// unregister and persist // unregister and persist
p2PService.removeDecryptedDirectMessageListener(getTradeProtocol(trade)); p2PService.removeDecryptedDirectMessageListener(getTradeProtocol(trade));

View File

@ -114,7 +114,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
protected void onTradeMessage(TradeMessage message, NodeAddress peerNodeAddress) { protected void onTradeMessage(TradeMessage message, NodeAddress peerNodeAddress) {
log.info("Received {} as TradeMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getTradeId(), message.getUid()); log.info("Received {} as TradeMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getTradeId(), message.getUid());
new Thread(() -> handle(message, peerNodeAddress)).start(); HavenoUtils.submitToThread(() -> handle(message, peerNodeAddress), trade.getId());
} }
protected void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) { protected void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) {
@ -264,9 +264,9 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
} }
private void maybeSendDepositsConfirmedMessage() { private void maybeSendDepositsConfirmedMessage() {
new Thread(() -> maybeSendDepositsConfirmedMessages()).start(); HavenoUtils.submitToThread(() -> maybeSendDepositsConfirmedMessages(), trade.getId());
EasyBind.subscribe(trade.stateProperty(), state -> { EasyBind.subscribe(trade.stateProperty(), state -> {
new Thread(() -> maybeSendDepositsConfirmedMessages()).start(); HavenoUtils.submitToThread(() -> maybeSendDepositsConfirmedMessages(), trade.getId());
}); });
} }
@ -279,7 +279,9 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
} }
log.warn("Reprocessing payment received message for {} {}", trade.getClass().getSimpleName(), trade.getId()); log.warn("Reprocessing payment received message for {} {}", trade.getClass().getSimpleName(), trade.getId());
new Thread(() -> handle(trade.getSeller().getPaymentReceivedMessage(), trade.getSeller().getPaymentReceivedMessage().getSenderNodeAddress(), reprocessOnError)).start(); HavenoUtils.submitToThread(() -> {
handle(trade.getSeller().getPaymentReceivedMessage(), trade.getSeller().getPaymentReceivedMessage().getSenderNodeAddress(), reprocessOnError);
}, trade.getId());
} }
} }
@ -351,9 +353,10 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
.executeTasks(true); .executeTasks(true);
awaitTradeLatch(); awaitTradeLatch();
} else { } else {
// process sign contract request after multisig created // process sign contract request after multisig created
EasyBind.subscribe(trade.stateProperty(), state -> { EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.MULTISIG_COMPLETED) new Thread(() -> handleSignContractRequest(message, sender)).start(); // process notification without trade lock if (state == Trade.State.MULTISIG_COMPLETED) HavenoUtils.submitToThread(() -> handleSignContractRequest(message, sender), trade.getId()); // process notification without trade lock
}); });
} }
} }
@ -393,9 +396,10 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
.executeTasks(true); .executeTasks(true);
awaitTradeLatch(); awaitTradeLatch();
} else { } else {
// process sign contract response after contract signed // process sign contract response after contract signed
EasyBind.subscribe(trade.stateProperty(), state -> { EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNED) new Thread(() -> handleSignContractResponse(message, sender)).start(); // process notification without trade lock if (state == Trade.State.CONTRACT_SIGNED) HavenoUtils.submitToThread(() -> handleSignContractResponse(message, sender), trade.getId()); // process notification without trade lock
}); });
} }
} }

View File

@ -37,7 +37,7 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask {
runInterceptHook(); runInterceptHook();
// check connection // check connection
trade.checkDaemonConnection(); trade.checkAndVerifyDaemonConnection();
// handle first time preparation // handle first time preparation
if (trade.getArbitrator().getPaymentReceivedMessage() == null) { if (trade.getArbitrator().getPaymentReceivedMessage() == null) {

View File

@ -687,7 +687,9 @@ public class XmrWalletService {
private void initialize() { private void initialize() {
// listen for connection changes // listen for connection changes
xmrConnectionService.addConnectionListener(newConnection -> onConnectionChanged(newConnection)); xmrConnectionService.addConnectionListener(connection -> {
HavenoUtils.submitToThread(() -> onConnectionChanged(connection), THREAD_ID);
});
// wait for monerod to sync // wait for monerod to sync
if (xmrConnectionService.downloadPercentageProperty().get() != 1) { if (xmrConnectionService.downloadPercentageProperty().get() != 1) {
@ -937,7 +939,7 @@ public class XmrWalletService {
// sync wallet on new thread // sync wallet on new thread
if (connection != null) { if (connection != null) {
wallet.getDaemonConnection().setPrintStackTrace(PRINT_STACK_TRACE); wallet.getDaemonConnection().setPrintStackTrace(PRINT_STACK_TRACE);
HavenoUtils.submitToThread(() -> { HavenoUtils.submitToPool(() -> {
synchronized (walletLock) { synchronized (walletLock) {
try { try {
if (Boolean.TRUE.equals(connection.isConnected())) wallet.sync(); if (Boolean.TRUE.equals(connection.isConnected())) wallet.sync();
@ -946,7 +948,7 @@ public class XmrWalletService {
log.warn("Failed to sync main wallet after setting daemon connection: " + e.getMessage()); log.warn("Failed to sync main wallet after setting daemon connection: " + e.getMessage());
} }
} }
}, THREAD_ID); });
} }
log.info("Done setting main wallet daemon connection: " + (connection == null ? null : connection.getUri())); log.info("Done setting main wallet daemon connection: " + (connection == null ? null : connection.getUri()));