fix app hanging after connection change with parallel handling
This commit is contained in:
parent
5e00612f66
commit
5fc67ec65a
@ -53,6 +53,7 @@ public final class CoreMoneroConnectionsService {
|
|||||||
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
|
private final Object listenersLock = new Object();
|
||||||
private final Config config;
|
private final Config config;
|
||||||
private final CoreContext coreContext;
|
private final CoreContext coreContext;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
@ -146,7 +147,7 @@ public final class CoreMoneroConnectionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addConnectionListener(MoneroConnectionManagerListener listener) {
|
public void addConnectionListener(MoneroConnectionManagerListener listener) {
|
||||||
synchronized (lock) {
|
synchronized (listenersLock) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -512,9 +513,11 @@ public final class CoreMoneroConnectionsService {
|
|||||||
}
|
}
|
||||||
updatePolling();
|
updatePolling();
|
||||||
|
|
||||||
// notify listeners
|
// notify listeners in parallel
|
||||||
synchronized (lock) {
|
synchronized (listenersLock) {
|
||||||
for (MoneroConnectionManagerListener listener : listeners) listener.onConnectionChanged(currentConnection);
|
for (MoneroConnectionManagerListener listener : listeners) {
|
||||||
|
new Thread(() -> listener.onConnectionChanged(currentConnection)).start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,64 +540,68 @@ public final class CoreMoneroConnectionsService {
|
|||||||
|
|
||||||
private void stopPolling() {
|
private void stopPolling() {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (daemonPollLooper != null) daemonPollLooper.stop();
|
if (daemonPollLooper != null) {
|
||||||
|
daemonPollLooper.stop();
|
||||||
|
daemonPollLooper = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pollDaemonInfo() {
|
private void pollDaemonInfo() {
|
||||||
if (isShutDownStarted) return;
|
synchronized (lock) {
|
||||||
try {
|
if (isShutDownStarted) return;
|
||||||
log.debug("Polling daemon info");
|
try {
|
||||||
if (daemon == null) throw new RuntimeException("No daemon connection");
|
log.debug("Polling daemon info");
|
||||||
synchronized (this) {
|
if (daemon == null) throw new RuntimeException("No daemon connection");
|
||||||
lastInfo = daemon.getInfo();
|
lastInfo = daemon.getInfo();
|
||||||
}
|
chainHeight.set(lastInfo.getTargetHeight() == 0 ? lastInfo.getHeight() : lastInfo.getTargetHeight());
|
||||||
chainHeight.set(lastInfo.getTargetHeight() == 0 ? lastInfo.getHeight() : lastInfo.getTargetHeight());
|
|
||||||
|
|
||||||
// set peer connections
|
// set peer connections
|
||||||
// TODO: peers often uknown due to restricted RPC call, skipping call to get peer connections
|
// TODO: peers often uknown due to restricted RPC call, skipping call to get peer connections
|
||||||
// try {
|
// try {
|
||||||
// peers.set(getOnlinePeers());
|
// peers.set(getOnlinePeers());
|
||||||
// } catch (Exception err) {
|
// } catch (Exception err) {
|
||||||
// // TODO: peers unknown due to restricted RPC call
|
// // TODO: peers unknown due to restricted RPC call
|
||||||
// }
|
// }
|
||||||
// numPeers.set(peers.get().size());
|
// numPeers.set(peers.get().size());
|
||||||
numPeers.set(lastInfo.getNumOutgoingConnections() + lastInfo.getNumIncomingConnections());
|
numPeers.set(lastInfo.getNumOutgoingConnections() + lastInfo.getNumIncomingConnections());
|
||||||
peers.set(new ArrayList<MoneroPeer>());
|
peers.set(new ArrayList<MoneroPeer>());
|
||||||
|
|
||||||
// handle error recovery
|
// handle error recovery
|
||||||
if (lastErrorTimestamp != null) {
|
if (lastErrorTimestamp != null) {
|
||||||
log.info("Successfully fetched daemon info after previous error");
|
log.info("Successfully fetched daemon info after previous error");
|
||||||
lastErrorTimestamp = null;
|
lastErrorTimestamp = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update and notify connected state
|
// update and notify connected state
|
||||||
if (!Boolean.TRUE.equals(connectionManager.isConnected())) {
|
if (!Boolean.TRUE.equals(connectionManager.isConnected())) {
|
||||||
connectionManager.checkConnection();
|
connectionManager.checkConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear error message
|
// clear error message
|
||||||
if (Boolean.TRUE.equals(connectionManager.isConnected()) && HavenoUtils.havenoSetup != null) {
|
if (Boolean.TRUE.equals(connectionManager.isConnected()) && HavenoUtils.havenoSetup != null) {
|
||||||
HavenoUtils.havenoSetup.getWalletServiceErrorMsg().set(null);
|
HavenoUtils.havenoSetup.getWalletServiceErrorMsg().set(null);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
||||||
// log error message periodically
|
// skip if shut down or connected
|
||||||
if ((lastErrorTimestamp == null || System.currentTimeMillis() - lastErrorTimestamp > MIN_ERROR_LOG_PERIOD_MS)) {
|
if (isShutDownStarted || Boolean.TRUE.equals(isConnected())) return;
|
||||||
lastErrorTimestamp = System.currentTimeMillis();
|
|
||||||
log.warn("Could not update daemon info: " + e.getMessage());
|
// log error message periodically
|
||||||
if (DevEnv.isDevMode()) e.printStackTrace();
|
if ((lastErrorTimestamp == null || System.currentTimeMillis() - lastErrorTimestamp > MIN_ERROR_LOG_PERIOD_MS)) {
|
||||||
}
|
lastErrorTimestamp = System.currentTimeMillis();
|
||||||
|
log.warn("Could not update daemon info: " + e.getMessage());
|
||||||
|
if (DevEnv.isDevMode()) e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
// check connection which notifies of changes
|
// check connection which notifies of changes
|
||||||
synchronized (this) {
|
|
||||||
if (connectionManager.getAutoSwitch()) connectionManager.setConnection(connectionManager.getBestAvailableConnection());
|
if (connectionManager.getAutoSwitch()) connectionManager.setConnection(connectionManager.getBestAvailableConnection());
|
||||||
else connectionManager.checkConnection();
|
else connectionManager.checkConnection();
|
||||||
}
|
|
||||||
|
|
||||||
// set error message
|
// set error message
|
||||||
if (!Boolean.TRUE.equals(connectionManager.isConnected()) && HavenoUtils.havenoSetup != null) {
|
if (!Boolean.TRUE.equals(connectionManager.isConnected()) && HavenoUtils.havenoSetup != null) {
|
||||||
HavenoUtils.havenoSetup.getWalletServiceErrorMsg().set(e.getMessage());
|
HavenoUtils.havenoSetup.getWalletServiceErrorMsg().set(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -587,8 +587,10 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
getArbitrator().setPubKeyRing(arbitrator.getPubKeyRing());
|
getArbitrator().setPubKeyRing(arbitrator.getPubKeyRing());
|
||||||
});
|
});
|
||||||
|
|
||||||
// listen to daemon connection
|
// handle daemon changes with max parallelization
|
||||||
xmrWalletService.getConnectionsService().addConnectionListener(newConnection -> onConnectionChanged(newConnection));
|
xmrWalletService.getConnectionsService().addConnectionListener(newConnection -> {
|
||||||
|
HavenoUtils.submitTask(() -> onConnectionChanged(newConnection));
|
||||||
|
});
|
||||||
|
|
||||||
// check if done
|
// check if done
|
||||||
if (isPayoutUnlocked()) {
|
if (isPayoutUnlocked()) {
|
||||||
|
@ -3,7 +3,6 @@ package haveno.core.xmr.wallet;
|
|||||||
import com.google.common.util.concurrent.Service.State;
|
import com.google.common.util.concurrent.Service.State;
|
||||||
import com.google.inject.name.Named;
|
import com.google.inject.name.Named;
|
||||||
|
|
||||||
import common.utils.GenUtils;
|
|
||||||
import common.utils.JsonUtils;
|
import common.utils.JsonUtils;
|
||||||
import haveno.common.UserThread;
|
import haveno.common.UserThread;
|
||||||
import haveno.common.config.Config;
|
import haveno.common.config.Config;
|
||||||
@ -97,6 +96,7 @@ public class XmrWalletService {
|
|||||||
private static final String ADDRESS_FILE_POSTFIX = ".address.txt";
|
private static final String ADDRESS_FILE_POSTFIX = ".address.txt";
|
||||||
private static final int NUM_MAX_BACKUP_WALLETS = 1;
|
private static final int NUM_MAX_BACKUP_WALLETS = 1;
|
||||||
private static final int MONERO_LOG_LEVEL = 0;
|
private static final int MONERO_LOG_LEVEL = 0;
|
||||||
|
private static final int MAX_SYNC_ATTEMPTS = 3;
|
||||||
private static final boolean PRINT_STACK_TRACE = false;
|
private static final boolean PRINT_STACK_TRACE = false;
|
||||||
|
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
@ -651,14 +651,20 @@ public class XmrWalletService {
|
|||||||
|
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
|
|
||||||
|
// set and listen to daemon connection
|
||||||
|
connectionsService.addConnectionListener(newConnection -> {
|
||||||
|
onConnectionChanged(newConnection);
|
||||||
|
});
|
||||||
|
|
||||||
// initialize main wallet if connected or previously created
|
// initialize main wallet if connected or previously created
|
||||||
maybeInitMainWallet(true);
|
maybeInitMainWallet(true);
|
||||||
|
|
||||||
// set and listen to daemon connection
|
|
||||||
connectionsService.addConnectionListener(newConnection -> onConnectionChanged(newConnection));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeInitMainWallet(boolean sync) {
|
private void maybeInitMainWallet(boolean sync) {
|
||||||
|
maybeInitMainWallet(sync, MAX_SYNC_ATTEMPTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeInitMainWallet(boolean sync, int numAttempts) {
|
||||||
synchronized (walletLock) {
|
synchronized (walletLock) {
|
||||||
|
|
||||||
// open or create wallet main wallet
|
// open or create wallet main wallet
|
||||||
@ -676,52 +682,47 @@ public class XmrWalletService {
|
|||||||
// sync wallet and register listener
|
// sync wallet and register listener
|
||||||
if (wallet != null) {
|
if (wallet != null) {
|
||||||
log.info("Monero wallet uri={}, path={}", wallet.getRpcConnection().getUri(), wallet.getPath());
|
log.info("Monero wallet uri={}, path={}", wallet.getRpcConnection().getUri(), wallet.getPath());
|
||||||
if (sync) {
|
if (sync && numAttempts > 0) {
|
||||||
int maxAttempts = 3;
|
try {
|
||||||
for (int i = 0; i < maxAttempts; i++) {
|
|
||||||
try {
|
// sync main wallet
|
||||||
|
log.info("Syncing main wallet");
|
||||||
// sync main wallet
|
long time = System.currentTimeMillis();
|
||||||
log.info("Syncing main wallet");
|
wallet.sync(); // blocking
|
||||||
long time = System.currentTimeMillis();
|
wasWalletSynced = true;
|
||||||
wallet.sync(); // blocking
|
log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms");
|
||||||
wasWalletSynced = true;
|
wallet.startSyncing(connectionsService.getRefreshPeriodMs());
|
||||||
log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms");
|
if (getMoneroNetworkType() != MoneroNetworkType.MAINNET) log.info("Monero wallet balance={}, unlocked balance={}", wallet.getBalance(0), wallet.getUnlockedBalance(0));
|
||||||
wallet.startSyncing(connectionsService.getRefreshPeriodMs());
|
|
||||||
if (getMoneroNetworkType() != MoneroNetworkType.MAINNET) log.info("Monero wallet balance={}, unlocked balance={}", wallet.getBalance(0), wallet.getUnlockedBalance(0));
|
// reapply connection after wallet synced
|
||||||
|
onConnectionChanged(connectionsService.getConnection());
|
||||||
// reapply connection after wallet synced
|
|
||||||
onConnectionChanged(connectionsService.getConnection());
|
|
||||||
|
|
||||||
// TODO: using this to signify both daemon and wallet synced, use separate sync handlers?
|
// TODO: using this to signify both daemon and wallet synced, use separate sync handlers?
|
||||||
connectionsService.doneDownload();
|
connectionsService.doneDownload();
|
||||||
|
|
||||||
// notify setup that main wallet is initialized
|
// notify setup that main wallet is initialized
|
||||||
// TODO: app fully initializes after this is set to true, even though wallet might not be initialized if unconnected. wallet will be created when connection detected
|
// TODO: app fully initializes after this is set to true, even though wallet might not be initialized if unconnected. wallet will be created when connection detected
|
||||||
// refactor startup to call this and sync off main thread? but the calls to e.g. getBalance() fail with 'wallet and network is not yet initialized'
|
// refactor startup to call this and sync off main thread? but the calls to e.g. getBalance() fail with 'wallet and network is not yet initialized'
|
||||||
|
HavenoUtils.havenoSetup.getWalletInitialized().set(true);
|
||||||
|
|
||||||
|
// save but skip backup on initialization
|
||||||
|
saveMainWallet(false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Error syncing main wallet: {}", e.getMessage());
|
||||||
|
if (numAttempts <= 1) {
|
||||||
|
log.warn("Failed to sync main wallet. Opening app without syncing", numAttempts);
|
||||||
HavenoUtils.havenoSetup.getWalletInitialized().set(true);
|
HavenoUtils.havenoSetup.getWalletInitialized().set(true);
|
||||||
|
|
||||||
// save but skip backup on initialization
|
|
||||||
saveMainWallet(false);
|
saveMainWallet(false);
|
||||||
break;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("Error syncing main wallet: {}", e.getMessage());
|
|
||||||
if (i == maxAttempts - 1) {
|
|
||||||
log.warn("Failed to sync main wallet after {} attempts. Opening app without syncing", maxAttempts);
|
|
||||||
HavenoUtils.havenoSetup.getWalletInitialized().set(true);
|
|
||||||
saveMainWallet(false);
|
|
||||||
|
|
||||||
// reschedule to init main wallet
|
// reschedule to init main wallet
|
||||||
UserThread.runAfter(() -> {
|
UserThread.runAfter(() -> {
|
||||||
new Thread(() -> {
|
new Thread(() -> maybeInitMainWallet(true, MAX_SYNC_ATTEMPTS)).start();
|
||||||
log.warn("Restarting attempts to sync main wallet");
|
}, connectionsService.getRefreshPeriodMs() / 1000);
|
||||||
maybeInitMainWallet(true);
|
} else {
|
||||||
});
|
log.warn("Trying again in {} seconds", connectionsService.getRefreshPeriodMs() / 1000);
|
||||||
}, connectionsService.getRefreshPeriodMs() / 1000);
|
UserThread.runAfter(() -> {
|
||||||
} else {
|
new Thread(() -> maybeInitMainWallet(true, numAttempts - 1)).start();
|
||||||
log.warn("Trying again in {} seconds", connectionsService.getRefreshPeriodMs() / 1000);
|
}, connectionsService.getRefreshPeriodMs() / 1000);
|
||||||
GenUtils.waitFor(connectionsService.getRefreshPeriodMs());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user