From 39d9d7d071dd9ed7a942578a7db520ac0ec08f5e Mon Sep 17 00:00:00 2001 From: xiphon Date: Fri, 9 Oct 2020 12:04:53 +0000 Subject: [PATCH] Wallet: rework wallet refreshing --- main.qml | 2 +- src/libwalletqt/Wallet.cpp | 111 +++++++++++++++++++++++++++---------- src/libwalletqt/Wallet.h | 24 ++++---- src/qt/FutureScheduler.cpp | 5 ++ src/qt/FutureScheduler.h | 3 +- 5 files changed, 100 insertions(+), 45 deletions(-) diff --git a/main.qml b/main.qml index 11aa93f3..377316e3 100644 --- a/main.qml +++ b/main.qml @@ -1895,7 +1895,7 @@ ApplicationWindow { repeat: true running: persistentSettings.autosave onTriggered: { - if (currentWallet) { + if (currentWallet && !currentWallet.refreshing) { currentWallet.storeAsync(function(success) { if (success) { appWindow.showStatusMessage(qsTr("Autosaved the wallet"), 3); diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 508f105e..454dca29 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -27,6 +27,11 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Wallet.h" + +#include +#include +#include + #include "PendingTransaction.h" #include "UnsignedTransaction.h" #include "TransactionHistory.h" @@ -50,6 +55,8 @@ #include #include +#include "qt/ScopeGuard.h" + namespace { static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 5; static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30; @@ -124,6 +131,19 @@ bool Wallet::disconnected() const return m_disconnected; } +bool Wallet::refreshing() const +{ + return m_refreshing; +} + +void Wallet::refreshingSet(bool value) +{ + if (m_refreshing.exchange(value) != value) + { + emit refreshingChanged(); + } +} + void Wallet::setConnectionStatus(ConnectionStatus value) { if (m_connectionStatus == value) @@ -196,7 +216,7 @@ void Wallet::storeAsync(const QJSValue &callback, const QString &path /* = "" */ { const auto future = m_scheduler.run( [this, path] { - QMutexLocker locker(&m_storeMutex); + QMutexLocker locker(&m_asyncMutex); return QJSValueList({m_walletImpl->store(path.toStdString())}); }, @@ -263,7 +283,7 @@ void Wallet::initAsync( emit walletCreationHeightChanged(); qDebug() << "init async finished - starting refresh"; connected(true); - m_walletImpl->startRefresh(); + startRefresh(); } }); if (future.first) @@ -466,41 +486,37 @@ bool Wallet::importKeyImages(const QString& path) return m_walletImpl->importKeyImages(path.toStdString()); } -bool Wallet::refresh() +bool Wallet::refresh(bool historyAndSubaddresses /* = true */) { - bool result = m_walletImpl->refresh(); - m_history->refresh(currentSubaddressAccount()); - m_subaddress->refresh(currentSubaddressAccount()); - m_subaddressAccount->getAll(); - if (result) - emit updated(); - return result; + refreshingSet(true); + const auto cleanup = sg::make_scope_guard([this]() noexcept { + refreshingSet(false); + }); + + { + QMutexLocker locker(&m_asyncMutex); + + bool result = m_walletImpl->refresh(); + if (historyAndSubaddresses) + { + m_history->refresh(currentSubaddressAccount()); + m_subaddress->refresh(currentSubaddressAccount()); + m_subaddressAccount->getAll(); + } + if (result) + emit updated(); + return result; + } } -void Wallet::refreshAsync() +void Wallet::startRefresh() { - qDebug() << "refresh async"; - m_walletImpl->refreshAsync(); + m_refreshEnabled = true; } -void Wallet::setAutoRefreshInterval(int seconds) +void Wallet::pauseRefresh() { - m_walletImpl->setAutoRefreshInterval(seconds); -} - -int Wallet::autoRefreshInterval() const -{ - return m_walletImpl->autoRefreshInterval(); -} - -void Wallet::startRefresh() const -{ - m_walletImpl->startRefresh(); -} - -void Wallet::pauseRefresh() const -{ - m_walletImpl->pauseRefresh(); + m_refreshEnabled = false; } PendingTransaction *Wallet::createTransaction(const QString &dst_addr, const QString &payment_id, @@ -874,6 +890,8 @@ bool Wallet::parse_uri(const QString &uri, QString &address, QString &payment_id bool Wallet::rescanSpent() { + QMutexLocker locker(&m_asyncMutex); + return m_walletImpl->rescanSpent(); } @@ -1041,6 +1059,8 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) , m_subaddressModel(nullptr) , m_subaddressAccount(nullptr) , m_subaddressAccountModel(nullptr) + , m_refreshEnabled(false) + , m_refreshing(false) , m_scheduler(this) { m_history = new TransactionHistory(m_walletImpl->history(), this); @@ -1058,6 +1078,8 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) m_connectionStatusRunning = false; m_daemonUsername = ""; m_daemonPassword = ""; + + startRefreshThread(); } Wallet::~Wallet() @@ -1090,3 +1112,32 @@ Wallet::~Wallet() m_walletListener = NULL; qDebug("m_walletImpl deleted"); } + +void Wallet::startRefreshThread() +{ + const auto future = m_scheduler.run([this] { + static constexpr const size_t refreshIntervalSec = 10; + static constexpr const size_t intervalResolutionMs = 100; + + auto last = std::chrono::steady_clock::now(); + while (!m_scheduler.stopping()) + { + if (m_refreshEnabled) + { + const auto now = std::chrono::steady_clock::now(); + const auto elapsed = std::chrono::duration_cast(now - last).count(); + if (elapsed >= refreshIntervalSec) + { + refresh(false); + last = std::chrono::steady_clock::now(); + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(intervalResolutionMs)); + } + }); + if (!future.first) + { + throw std::runtime_error("failed to start auto refresh thread"); + } +} diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index b099a011..42e30336 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -63,6 +63,7 @@ class Wallet : public QObject, public PassprasePrompter { Q_OBJECT Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged) + Q_PROPERTY(bool refreshing READ refreshing NOTIFY refreshingChanged) Q_PROPERTY(QString seed READ getSeed) Q_PROPERTY(QString seedLanguage READ getSeedLanguage) Q_PROPERTY(Status status READ status) @@ -207,20 +208,11 @@ public: Q_INVOKABLE bool importKeyImages(const QString& path); //! refreshes the wallet - Q_INVOKABLE bool refresh(); - - //! refreshes the wallet asynchronously - Q_INVOKABLE void refreshAsync(); - - //! setup auto-refresh interval in seconds - Q_INVOKABLE void setAutoRefreshInterval(int seconds); - - //! return auto-refresh interval in seconds - Q_INVOKABLE int autoRefreshInterval() const; + Q_INVOKABLE bool refresh(bool historyAndSubaddresses = true); // pause/resume refresh - Q_INVOKABLE void startRefresh() const; - Q_INVOKABLE void pauseRefresh() const; + Q_INVOKABLE void startRefresh(); + Q_INVOKABLE void pauseRefresh(); //! creates transaction Q_INVOKABLE PendingTransaction * createTransaction(const QString &dst_addr, const QString &payment_id, @@ -394,6 +386,7 @@ signals: void currentSubaddressAccountChanged() const; void disconnectedChanged() const; void proxyAddressChanged() const; + void refreshingChanged() const; private: Wallet(QObject * parent = nullptr); @@ -421,9 +414,12 @@ private: const QString& proxyAddress); bool disconnected() const; + bool refreshing() const; + void refreshingSet(bool value); void setConnectionStatus(ConnectionStatus value); QString getProxyAddress() const; void setProxyAddress(QString address); + void startRefreshThread(); private: friend class WalletManager; @@ -454,15 +450,17 @@ private: mutable SubaddressModel * m_subaddressModel; SubaddressAccount * m_subaddressAccount; mutable SubaddressAccountModel * m_subaddressAccountModel; + QMutex m_asyncMutex; QMutex m_connectionStatusMutex; bool m_connectionStatusRunning; QString m_daemonUsername; QString m_daemonPassword; QString m_proxyAddress; mutable QMutex m_proxyMutex; + std::atomic m_refreshEnabled; + std::atomic m_refreshing; WalletListenerImpl *m_walletListener; FutureScheduler m_scheduler; - QMutex m_storeMutex; }; diff --git a/src/qt/FutureScheduler.cpp b/src/qt/FutureScheduler.cpp index b15101e4..f83b5e7f 100644 --- a/src/qt/FutureScheduler.cpp +++ b/src/qt/FutureScheduler.cpp @@ -65,6 +65,11 @@ QPair> FutureScheduler::run(std::function> run(std::function function) noexcept; QPair> run(std::function function, const QJSValue &callback); + bool stopping() const noexcept; private: bool add() noexcept; @@ -59,7 +60,7 @@ private: size_t Alive; QWaitCondition Condition; QMutex Mutex; - bool Stopping; + std::atomic Stopping; }; #endif // FUTURE_SCHEDULER_H