From 2ed59f41ed7f02cf99f75f34637bfa75ae427097 Mon Sep 17 00:00:00 2001 From: Jaquee Date: Sat, 25 Feb 2017 14:57:39 +0100 Subject: [PATCH 1/3] Daemon manager improvements --- main.qml | 6 +-- pages/Settings.qml | 2 +- src/daemon/DaemonManager.cpp | 85 +++++++++++++++++++++++++++++++----- src/daemon/DaemonManager.h | 5 +++ 4 files changed, 83 insertions(+), 15 deletions(-) diff --git a/main.qml b/main.qml index 843c43b4..553c9521 100644 --- a/main.qml +++ b/main.qml @@ -235,7 +235,6 @@ ApplicationWindow { } function connectWallet(wallet) { - showProcessingSplash("Please wait...") currentWallet = wallet updateSyncing(false) @@ -341,9 +340,6 @@ ApplicationWindow { function onWalletRefresh() { console.log(">>> wallet refreshed") - if (splash.visible) { - hideProcessingSplash() - } // Daemon connected leftPanel.networkStatus.connected = currentWallet.connected() @@ -398,6 +394,7 @@ ApplicationWindow { console.log("daemon started"); daemonRunning = true; hideProcessingSplash(); + currentWallet.connected(true); } function onDaemonStopped(){ console.log("daemon stopped"); @@ -1233,6 +1230,7 @@ ApplicationWindow { } onClosing: { // Close wallet non async on exit + daemonManager.exit(); walletManager.closeWallet(); } diff --git a/pages/Settings.qml b/pages/Settings.qml index 651357cb..0bd1816c 100644 --- a/pages/Settings.qml +++ b/pages/Settings.qml @@ -407,7 +407,7 @@ Rectangle { console.log("Settings page loaded"); initSettings(); viewOnly = currentWallet.viewOnly; - daemonManager.running(persistentSettings.testnet) + appWindow.daemonRunning = daemonManager.running(persistentSettings.testnet) } // fires only once diff --git a/src/daemon/DaemonManager.cpp b/src/daemon/DaemonManager.cpp index 606448d9..855058eb 100644 --- a/src/daemon/DaemonManager.cpp +++ b/src/daemon/DaemonManager.cpp @@ -67,11 +67,22 @@ bool DaemonManager::start(const QString &flags, bool testnet) // add state changed listener connect(m_daemon,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState))); - if (!started) { + if (!started) qDebug() << "Daemon start error: " + m_daemon->errorString(); - } else { - emit daemonStarted(); - } + + // Start start watcher + QFuture future = QtConcurrent::run(this, &DaemonManager::startWatcher, testnet); + QFutureWatcher * watcher = new QFutureWatcher(); + connect(watcher, &QFutureWatcher::finished, + this, [this, watcher]() { + QFuture future = watcher->future(); + watcher->deleteLater(); + if(future.result()) { + emit daemonStarted(); + } + }); + watcher->setFuture(future); + return started; } @@ -79,13 +90,63 @@ bool DaemonManager::start(const QString &flags, bool testnet) bool DaemonManager::stop(bool testnet) { QString message; - bool stopped = sendCommand("exit",testnet,message); + sendCommand("exit",testnet,message); qDebug() << message; - if(stopped) - emit daemonStopped(); - return stopped; + + // Start stop watcher - Will kill if not shutting down + QFuture future = QtConcurrent::run(this, &DaemonManager::stopWatcher, testnet); + QFutureWatcher * watcher = new QFutureWatcher(); + connect(watcher, &QFutureWatcher::finished, + this, [this, watcher]() { + QFuture future = watcher->future(); + watcher->deleteLater(); + if(future.result()) { + emit daemonStopped(); + } + }); + watcher->setFuture(future); + + return true; +} + +bool DaemonManager::startWatcher(bool testnet) const +{ + // Check if daemon is started every 2 seconds + while(true && !m_app_exit) { + QThread::sleep(2); + if(!running(testnet)) { + qDebug() << "daemon not running. checking again in 2 seconds."; + } else + return true; + } + return false; +} + +bool DaemonManager::stopWatcher(bool testnet) const +{ + // Check if daemon is running every 2 seconds. Kill if still running after 10 seconds + int counter = 0; + while(true && !m_app_exit) { + QThread::sleep(2); + counter++; + if(running(testnet)) { + qDebug() << "Daemon still running. " << counter; + if(counter >= 5) { + qDebug() << "Killing it! "; +#ifdef Q_OS_WIN + QProcess::execute("taskkill /F /IM monerod.exe"); +#else + QProcess::execute("pkill monerod"); +#endif + } + + } else + return true; + } + return false; } + void DaemonManager::stateChanged(QProcess::ProcessState state) { qDebug() << "STATE CHANGED: " << state; @@ -124,10 +185,8 @@ bool DaemonManager::running(bool testnet) const // `./monerod status` returns BUSY when syncing. // Treat busy as connected, until fixed upstream. if (status.contains("Height:") || status.contains("BUSY") ) { - emit daemonStarted(); return true; } - emit daemonStopped(); return false; } bool DaemonManager::sendCommand(const QString &cmd,bool testnet) const @@ -155,6 +214,12 @@ bool DaemonManager::sendCommand(const QString &cmd,bool testnet, QString &messag return started; } +void DaemonManager::exit() +{ + qDebug("DaemonManager: exit()"); + m_app_exit = true; +} + DaemonManager::DaemonManager(QObject *parent) : QObject(parent) { diff --git a/src/daemon/DaemonManager.h b/src/daemon/DaemonManager.h index bd13ef60..b45b369f 100644 --- a/src/daemon/DaemonManager.h +++ b/src/daemon/DaemonManager.h @@ -20,9 +20,13 @@ public: Q_INVOKABLE bool running(bool testnet) const; // Send daemon command from qml and prints output in console window. Q_INVOKABLE bool sendCommand(const QString &cmd, bool testnet) const; + Q_INVOKABLE void exit(); private: + bool sendCommand(const QString &cmd, bool testnet, QString &message) const; + bool startWatcher(bool testnet) const; + bool stopWatcher(bool testnet) const; signals: void daemonStarted() const; void daemonStopped() const; @@ -41,6 +45,7 @@ private: bool initialized = false; QString m_monerod; bool m_has_daemon = true; + bool m_app_exit = false; }; From df60c8190bf2da1ffca6660392158fe8ee520cd6 Mon Sep 17 00:00:00 2001 From: Jaquee Date: Sat, 25 Feb 2017 19:49:09 +0100 Subject: [PATCH 2/3] add start/pauseRefresh() --- src/libwalletqt/Wallet.cpp | 10 ++++++++++ src/libwalletqt/Wallet.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 8913d4c3..2b176cf5 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -302,6 +302,16 @@ int Wallet::autoRefreshInterval() const return m_walletImpl->autoRefreshInterval(); } +void Wallet::startRefresh() const +{ + m_walletImpl->startRefresh(); +} + +void Wallet::pauseRefresh() const +{ + m_walletImpl->pauseRefresh(); +} + PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QString &payment_id, quint64 amount, quint32 mixin_count, PendingTransaction::Priority priority) diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index a11cabe9..c75f53b1 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -146,6 +146,10 @@ public: //! return auto-refresh interval in seconds Q_INVOKABLE int autoRefreshInterval() const; + // pause/resume refresh + Q_INVOKABLE void startRefresh() const; + Q_INVOKABLE void pauseRefresh() const; + //! creates transaction Q_INVOKABLE PendingTransaction * createTransaction(const QString &dst_addr, const QString &payment_id, quint64 amount, quint32 mixin_count, From 80210376f3fb80116f4592dd206247bf82fd7edc Mon Sep 17 00:00:00 2001 From: Jaquee Date: Sat, 25 Feb 2017 20:25:16 +0100 Subject: [PATCH 3/3] pause refresh while starting daemon + startup timeout --- main.qml | 19 +++++++++++++++++++ src/daemon/DaemonManager.cpp | 26 ++++++++++++++++++++------ src/daemon/DaemonManager.h | 1 + 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/main.qml b/main.qml index 553c9521..8b7c079d 100644 --- a/main.qml +++ b/main.qml @@ -380,6 +380,9 @@ ApplicationWindow { } function startDaemon(flags){ + // Pause refresh while starting daemon + currentWallet.pauseRefresh(); + appWindow.showProcessingSplash(qsTr("Waiting for daemon to start...")) daemonManager.start(flags, persistentSettings.testnet); persistentSettings.daemonFlags = flags @@ -395,6 +398,8 @@ ApplicationWindow { daemonRunning = true; hideProcessingSplash(); currentWallet.connected(true); + // resume refresh + currentWallet.startRefresh(); } function onDaemonStopped(){ console.log("daemon stopped"); @@ -403,6 +408,19 @@ ApplicationWindow { currentWallet.connected(true); } + function onDaemonStartFailure(){ + console.log("daemon start failed"); + hideProcessingSplash(); + // resume refresh + currentWallet.startRefresh(); + daemonRunning = false; + informationPopup.title = qsTr("Daemon failed to start") + translationManager.emptyString; + informationPopup.text = qsTr("Please check your wallet and daemon log for errors. You can also try to start %1 manually.").arg((isWindows)? "monerod.exe" : "monerod") + informationPopup.icon = StandardIcon.Critical + informationPopup.onCloseCallback = null + informationPopup.open(); + } + function onWalletNewBlock(blockHeight, targetHeight) { // Update progress bar leftPanel.progressBar.updateProgress(blockHeight,targetHeight); @@ -760,6 +778,7 @@ ApplicationWindow { walletManager.walletClosed.connect(onWalletClosed); daemonManager.daemonStarted.connect(onDaemonStarted); + daemonManager.daemonStartFailure.connect(onDaemonStartFailure); daemonManager.daemonStopped.connect(onDaemonStopped); if(!walletsFound()) { diff --git a/src/daemon/DaemonManager.cpp b/src/daemon/DaemonManager.cpp index 855058eb..47ad10cf 100644 --- a/src/daemon/DaemonManager.cpp +++ b/src/daemon/DaemonManager.cpp @@ -7,6 +7,11 @@ #include #include #include +#include + +namespace { + static const int DAEMON_START_TIMEOUT_SECONDS = 30; +} DaemonManager * DaemonManager::m_instance = nullptr; QStringList DaemonManager::m_clArgs; @@ -67,8 +72,11 @@ bool DaemonManager::start(const QString &flags, bool testnet) // add state changed listener connect(m_daemon,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState))); - if (!started) + if (!started) { qDebug() << "Daemon start error: " + m_daemon->errorString(); + emit daemonStartFailure(); + return false; + } // Start start watcher QFuture future = QtConcurrent::run(this, &DaemonManager::startWatcher, testnet); @@ -77,14 +85,15 @@ bool DaemonManager::start(const QString &flags, bool testnet) this, [this, watcher]() { QFuture future = watcher->future(); watcher->deleteLater(); - if(future.result()) { + if(future.result()) emit daemonStarted(); - } + else + emit daemonStartFailure(); }); watcher->setFuture(future); - return started; + return true; } bool DaemonManager::stop(bool testnet) @@ -112,12 +121,17 @@ bool DaemonManager::stop(bool testnet) bool DaemonManager::startWatcher(bool testnet) const { // Check if daemon is started every 2 seconds - while(true && !m_app_exit) { + QTime timer; + timer.restart(); + while(true && !m_app_exit && timer.elapsed() / 1000 < DAEMON_START_TIMEOUT_SECONDS ) { QThread::sleep(2); if(!running(testnet)) { qDebug() << "daemon not running. checking again in 2 seconds."; - } else + } else { + qDebug() << "daemon is started. Waiting 5 seconds to let daemon catch up"; + QThread::sleep(5); return true; + } } return false; } diff --git a/src/daemon/DaemonManager.h b/src/daemon/DaemonManager.h index b45b369f..428630e1 100644 --- a/src/daemon/DaemonManager.h +++ b/src/daemon/DaemonManager.h @@ -30,6 +30,7 @@ private: signals: void daemonStarted() const; void daemonStopped() const; + void daemonStartFailure() const; void daemonConsoleUpdated(QString message) const; public slots: