From be7810c5a877898d792cc7d4406dc05c3280b38f Mon Sep 17 00:00:00 2001 From: xiphon Date: Thu, 20 Jun 2019 20:28:59 +0000 Subject: qt: implement FutureScheduler, always await async code to complete --- src/libwalletqt/Wallet.cpp | 171 +++++++++++++------------------------- src/libwalletqt/Wallet.h | 12 +-- src/libwalletqt/WalletManager.cpp | 71 +++++----------- src/libwalletqt/WalletManager.h | 8 +- 4 files changed, 92 insertions(+), 170 deletions(-) (limited to 'src/libwalletqt') diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index ff0ada14..46f843de 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -150,13 +150,8 @@ NetworkType::Type Wallet::nettype() const void Wallet::updateConnectionStatusAsync() { - QFuture future = QtConcurrent::run(m_walletImpl, &Monero::Wallet::connected); - QFutureWatcher *connectionWatcher = new QFutureWatcher(); - - connect(connectionWatcher, &QFutureWatcher::finished, [=]() { - QFuture future = connectionWatcher->future(); - connectionWatcher->deleteLater(); - ConnectionStatus newStatus = static_cast(future.result()); + m_scheduler.run([this] { + ConnectionStatus newStatus = static_cast(m_walletImpl->connected()); if (newStatus != m_connectionStatus || !m_initialized) { m_initialized = true; m_connectionStatus = newStatus; @@ -166,7 +161,6 @@ void Wallet::updateConnectionStatusAsync() // Release lock m_connectionStatusRunning = false; }); - connectionWatcher->setFuture(future); } Wallet::ConnectionStatus Wallet::connected(bool forceCheck) @@ -246,15 +240,10 @@ void Wallet::initAsync(const QString &daemonAddress, quint64 upperTransactionLim emit connectionStatusChanged(m_connectionStatus); } - QFuture future = QtConcurrent::run(this, &Wallet::init, - daemonAddress, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight); - QFutureWatcher * watcher = new QFutureWatcher(); - - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher, daemonAddress, upperTransactionLimit, isRecovering, restoreHeight]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - if(future.result()){ + m_scheduler.run([this, daemonAddress, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight] { + bool success = init(daemonAddress, upperTransactionLimit, isRecovering, isRecoveringFromDevice, restoreHeight); + if (success) + { emit walletCreationHeightChanged(); qDebug() << "init async finished - starting refresh"; connected(true); @@ -262,7 +251,6 @@ void Wallet::initAsync(const QString &daemonAddress, quint64 upperTransactionLim } }); - watcher->setFuture(future); } //! create a view only wallet @@ -348,16 +336,32 @@ void Wallet::setSubaddressLabel(quint32 accountIndex, quint32 addressIndex, cons m_walletImpl->setSubaddressLabel(accountIndex, addressIndex, label.toStdString()); } -void Wallet::refreshHeightAsync() const +void Wallet::refreshHeightAsync() { - QtConcurrent::run([this] { - QFuture daemonHeight = QtConcurrent::run([this] { - return daemonBlockChainHeight(); + m_scheduler.run([this] { + quint64 daemonHeight; + QPair> daemonHeightFuture = m_scheduler.run([this, &daemonHeight] { + daemonHeight = daemonBlockChainHeight(); }); - QFuture targetHeight = QtConcurrent::run([this] { - return daemonBlockChainTargetHeight(); + if (!daemonHeightFuture.first) + { + return; + } + + quint64 targetHeight; + QPair> targetHeightFuture = m_scheduler.run([this, &targetHeight] { + targetHeight = daemonBlockChainTargetHeight(); }); - emit heightRefreshed(blockChainHeight(), daemonHeight.result(), targetHeight.result()); + if (!targetHeightFuture.first) + { + return; + } + + quint64 walletHeight = blockChainHeight(); + daemonHeightFuture.second.waitForFinished(); + targetHeightFuture.second.waitForFinished(); + + emit heightRefreshed(walletHeight, daemonHeight, targetHeight); }); } @@ -458,17 +462,10 @@ void Wallet::createTransactionAsync(const QString &dst_addr, const QString &paym quint64 amount, quint32 mixin_count, PendingTransaction::Priority priority) { - QFuture future = QtConcurrent::run(this, &Wallet::createTransaction, - dst_addr, payment_id,amount, mixin_count, priority); - QFutureWatcher * watcher = new QFutureWatcher(); - - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher,dst_addr,payment_id,mixin_count]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - emit transactionCreated(future.result(),dst_addr,payment_id,mixin_count); + m_scheduler.run([this, dst_addr, payment_id, amount, mixin_count, priority] { + PendingTransaction *tx = createTransaction(dst_addr, payment_id, amount, mixin_count, priority); + emit transactionCreated(tx, dst_addr, payment_id, mixin_count); }); - watcher->setFuture(future); } PendingTransaction *Wallet::createTransactionAll(const QString &dst_addr, const QString &payment_id, @@ -486,17 +483,10 @@ void Wallet::createTransactionAllAsync(const QString &dst_addr, const QString &p quint32 mixin_count, PendingTransaction::Priority priority) { - QFuture future = QtConcurrent::run(this, &Wallet::createTransactionAll, - dst_addr, payment_id, mixin_count, priority); - QFutureWatcher * watcher = new QFutureWatcher(); - - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher,dst_addr,payment_id,mixin_count]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - emit transactionCreated(future.result(),dst_addr,payment_id,mixin_count); + m_scheduler.run([this, dst_addr, payment_id, mixin_count, priority] { + PendingTransaction *tx = createTransactionAll(dst_addr, payment_id, mixin_count, priority); + emit transactionCreated(tx, dst_addr, payment_id, mixin_count); }); - watcher->setFuture(future); } PendingTransaction *Wallet::createSweepUnmixableTransaction() @@ -508,16 +498,10 @@ PendingTransaction *Wallet::createSweepUnmixableTransaction() void Wallet::createSweepUnmixableTransactionAsync() { - QFuture future = QtConcurrent::run(this, &Wallet::createSweepUnmixableTransaction); - QFutureWatcher * watcher = new QFutureWatcher(); - - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - emit transactionCreated(future.result(),"","",0); + m_scheduler.run([this] { + PendingTransaction *tx = createSweepUnmixableTransaction(); + emit transactionCreated(tx, "", "", 0); }); - watcher->setFuture(future); } UnsignedTransaction * Wallet::loadTxFile(const QString &fileName) @@ -539,18 +523,9 @@ bool Wallet::submitTxFile(const QString &fileName) const void Wallet::commitTransactionAsync(PendingTransaction *t) { - QStringList txid(t->txid()); - QFuture future = QtConcurrent::run(t, &PendingTransaction::commit); - - QFutureWatcher * watcher = new QFutureWatcher(); - - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher, t, txid]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - emit transactionCommitted(future.result(), t, txid); - }); - watcher->setFuture(future); + m_scheduler.run([this, t] { + emit transactionCommitted(t->commit(), t, t->txid()); + }); } void Wallet::disposeTransaction(PendingTransaction *t) @@ -662,23 +637,11 @@ QString Wallet::getTxKey(const QString &txid) const return QString::fromStdString(m_walletImpl->getTxKey(txid.toStdString())); } -void Wallet::getTxKeyAsync(const QString &txid, const QJSValue &ref) +void Wallet::getTxKeyAsync(const QString &txid, const QJSValue &callback) { - QFuture future = QtConcurrent::run(this, &Wallet::getTxKey, txid); - auto watcher = new QFutureWatcher(this); - - connect(watcher, &QFutureWatcher::finished, - this, [watcher, txid, ref]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - - auto txKey = future.result(); - if (ref.isCallable()){ - QJSValue cb(ref); - cb.call(QJSValueList {txid, txKey}); - } - }); - watcher->setFuture(future); + m_scheduler.run([this, txid] { + return QJSValueList({txid, getTxKey(txid)}); + }, callback); } QString Wallet::checkTxKey(const QString &txid, const QString &tx_key, const QString &address) @@ -699,23 +662,11 @@ QString Wallet::getTxProof(const QString &txid, const QString &address, const QS return QString::fromStdString(result); } -void Wallet::getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &ref) +void Wallet::getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &callback) { - QFuture future = QtConcurrent::run(this, &Wallet::getTxProof, txid, address, message); - auto watcher = new QFutureWatcher(this); - - connect(watcher, &QFutureWatcher::finished, - this, [watcher, txid, ref]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - - auto proof = future.result(); - if (ref.isCallable()){ - QJSValue cb(ref); - cb.call(QJSValueList {txid, proof}); - } - }); - watcher->setFuture(future); + m_scheduler.run([this, txid, address, message] { + return QJSValueList({txid, getTxProof(txid, address, message)}); + }, callback); } QString Wallet::checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature) @@ -737,23 +688,11 @@ Q_INVOKABLE QString Wallet::getSpendProof(const QString &txid, const QString &me return QString::fromStdString(result); } -void Wallet::getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &ref) +void Wallet::getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback) { - QFuture future = QtConcurrent::run(this, &Wallet::getSpendProof, txid, message); - auto watcher = new QFutureWatcher(this); - - connect(watcher, &QFutureWatcher::finished, - this, [watcher, txid, ref]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - - auto proof = future.result(); - if (ref.isCallable()){ - QJSValue cb(ref); - cb.call(QJSValueList {txid, proof}); - } - }); - watcher->setFuture(future); + m_scheduler.run([this, txid, message] { + return QJSValueList({txid, getSpendProof(txid, message)}); + }, callback); } Q_INVOKABLE QString Wallet::checkSpendProof(const QString &txid, const QString &message, const QString &signature) const @@ -1007,6 +946,7 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) , m_daemonBlockChainTargetHeightTtl(DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS) , m_connectionStatusTtl(WALLET_CONNECTION_STATUS_CACHE_TTL_SECONDS) , m_currentSubaddressAccount(0) + , m_scheduler(this) { m_history = new TransactionHistory(m_walletImpl->history(), this); m_addressBook = new AddressBook(m_walletImpl->addressBook(), this); @@ -1028,6 +968,9 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent) Wallet::~Wallet() { qDebug("~Wallet: Closing wallet"); + + m_scheduler.shutdownWaitForFinished(); + delete m_addressBook; m_addressBook = NULL; diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index f3726e91..95d4a954 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -37,6 +37,7 @@ #include #include "wallet/api/wallet2_api.h" // we need to have an access to the Monero::Wallet::Status enum here; +#include "qt/FutureScheduler.h" #include "PendingTransaction.h" // we need to have an access to the PendingTransaction::Priority enum here; #include "UnsignedTransaction.h" #include "NetworkType.h" @@ -182,7 +183,7 @@ public: //! returns if view only wallet Q_INVOKABLE bool viewOnly() const; - Q_INVOKABLE void refreshHeightAsync() const; + Q_INVOKABLE void refreshHeightAsync(); //! export/import key images Q_INVOKABLE bool exportKeyImages(const QString& path); @@ -290,13 +291,13 @@ public: Q_INVOKABLE bool setUserNote(const QString &txid, const QString ¬e); Q_INVOKABLE QString getUserNote(const QString &txid) const; Q_INVOKABLE QString getTxKey(const QString &txid) const; - Q_INVOKABLE void getTxKeyAsync(const QString &txid, const QJSValue &ref); + Q_INVOKABLE void getTxKeyAsync(const QString &txid, const QJSValue &callback); Q_INVOKABLE QString checkTxKey(const QString &txid, const QString &tx_key, const QString &address); Q_INVOKABLE QString getTxProof(const QString &txid, const QString &address, const QString &message) const; - Q_INVOKABLE void getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &ref); + Q_INVOKABLE void getTxProofAsync(const QString &txid, const QString &address, const QString &message, const QJSValue &callback); Q_INVOKABLE QString checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature); Q_INVOKABLE QString getSpendProof(const QString &txid, const QString &message) const; - Q_INVOKABLE void getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &ref); + Q_INVOKABLE void getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback); Q_INVOKABLE QString checkSpendProof(const QString &txid, const QString &message, const QString &signature) const; // Rescan spent outputs Q_INVOKABLE bool rescanSpent(); @@ -356,7 +357,7 @@ signals: // emitted when transaction is created async void transactionCreated(PendingTransaction * transaction, QString address, QString paymentId, quint32 mixinCount); - void connectionStatusChanged(ConnectionStatus status) const; + void connectionStatusChanged(int status) const; private: Wallet(QObject * parent = nullptr); @@ -406,6 +407,7 @@ private: QString m_daemonUsername; QString m_daemonPassword; Monero::WalletListener *m_walletListener; + FutureScheduler m_scheduler; }; diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index 790ad561..22a810b9 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -145,17 +145,9 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password, void WalletManager::openWalletAsync(const QString &path, const QString &password, NetworkType::Type nettype, quint64 kdfRounds) { - QFuture future = QtConcurrent::run(this, &WalletManager::openWallet, - path, password, nettype, kdfRounds); - QFutureWatcher * watcher = new QFutureWatcher(); - - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - emit walletOpened(future.result()); + m_scheduler.run([this, path, password, nettype, kdfRounds] { + emit walletOpened(openWallet(path, password, nettype, kdfRounds)); }); - watcher->setFuture(future); } @@ -216,21 +208,10 @@ Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString void WalletManager::createWalletFromDeviceAsync(const QString &path, const QString &password, NetworkType::Type nettype, const QString &deviceName, quint64 restoreHeight, const QString &subaddressLookahead) { - auto lmbd = [=](){ - return this->createWalletFromDevice(path, password, nettype, deviceName, restoreHeight, subaddressLookahead); - }; - - QFuture future = QtConcurrent::run(lmbd); - - QFutureWatcher * watcher = new QFutureWatcher(); - - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - emit walletCreated(future.result()); - }); - watcher->setFuture(future); + m_scheduler.run([this, path, password, nettype, deviceName, restoreHeight, subaddressLookahead] { + Wallet *wallet = createWalletFromDevice(path, password, nettype, deviceName, restoreHeight, subaddressLookahead); + emit walletCreated(wallet); + }); } QString WalletManager::closeWallet() @@ -249,16 +230,9 @@ QString WalletManager::closeWallet() void WalletManager::closeWalletAsync() { - QFuture future = QtConcurrent::run(this, &WalletManager::closeWallet); - QFutureWatcher * watcher = new QFutureWatcher(); - - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - emit walletClosed(future.result()); + m_scheduler.run([this] { + emit walletClosed(closeWallet()); }); - watcher->setFuture(future); } bool WalletManager::walletExists(const QString &path) const @@ -333,7 +307,7 @@ QString WalletManager::paymentIdFromAddress(const QString &address, NetworkType: void WalletManager::setDaemonAddressAsync(const QString &address) { - QtConcurrent::run([this, address] { + m_scheduler.run([this, address] { m_pimpl->setDaemonAddress(address.toStdString()); }); } @@ -376,9 +350,9 @@ bool WalletManager::isMining() const return m_pimpl->isMining(); } -void WalletManager::miningStatusAsync() const +void WalletManager::miningStatusAsync() { - QtConcurrent::run([this] { + m_scheduler.run([this] { emit miningStatus(isMining()); }); } @@ -488,19 +462,11 @@ bool WalletManager::saveQrCode(const QString &code, const QString &path) const return QRCodeImageProvider::genQrImage(code, &size).scaled(size.expandedTo(QSize(240, 240)), Qt::KeepAspectRatio).save(path, "PNG", 100); } -void WalletManager::checkUpdatesAsync(const QString &software, const QString &subdir) const +void WalletManager::checkUpdatesAsync(const QString &software, const QString &subdir) { - QFuture future = QtConcurrent::run(this, &WalletManager::checkUpdates, - software, subdir); - QFutureWatcher * watcher = new QFutureWatcher(); - connect(watcher, &QFutureWatcher::finished, - this, [this, watcher]() { - QFuture future = watcher->future(); - watcher->deleteLater(); - qDebug() << "Checking for updates - done"; - emit checkUpdatesComplete(future.result()); + m_scheduler.run([this, software, subdir] { + emit checkUpdatesComplete(checkUpdates(software, subdir)); }); - watcher->setFuture(future); } @@ -532,11 +498,18 @@ bool WalletManager::clearWalletCache(const QString &wallet_path) const return walletCache.rename(newFileName); } -WalletManager::WalletManager(QObject *parent) : QObject(parent) +WalletManager::WalletManager(QObject *parent) + : QObject(parent) + , m_scheduler(this) { m_pimpl = Monero::WalletManagerFactory::getWalletManager(); } +WalletManager::~WalletManager() +{ + m_scheduler.shutdownWaitForFinished(); +} + void WalletManager::onWalletPassphraseNeeded(Monero::Wallet *) { m_mutex_pass.lock(); diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index 3acfa845..18256b1c 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -37,6 +37,7 @@ #include #include #include +#include "qt/FutureScheduler.h" #include "NetworkType.h" class Wallet; @@ -151,7 +152,7 @@ public: Q_INVOKABLE bool localDaemonSynced() const; Q_INVOKABLE bool isDaemonLocal(const QString &daemon_address) const; - Q_INVOKABLE void miningStatusAsync() const; + Q_INVOKABLE void miningStatusAsync(); Q_INVOKABLE bool startMining(const QString &address, quint32 threads, bool backgroundMining, bool ignoreBattery); Q_INVOKABLE bool stopMining(); @@ -175,7 +176,7 @@ public: Q_INVOKABLE bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector &unknown_parameters, QString &error) const; Q_INVOKABLE QVariantMap parse_uri_to_object(const QString &uri) const; Q_INVOKABLE bool saveQrCode(const QString &, const QString &) const; - Q_INVOKABLE void checkUpdatesAsync(const QString &software, const QString &subdir) const; + Q_INVOKABLE void checkUpdatesAsync(const QString &software, const QString &subdir); Q_INVOKABLE QString checkUpdates(const QString &software, const QString &subdir) const; // clear/rename wallet cache @@ -200,6 +201,7 @@ private: friend class WalletPassphraseListenerImpl; explicit WalletManager(QObject *parent = 0); + ~WalletManager(); bool isMining() const; @@ -212,6 +214,8 @@ private: QMutex m_mutex_pass; QString m_passphrase; bool m_passphrase_abort; + + FutureScheduler m_scheduler; }; #endif // WALLETMANAGER_H -- cgit v1.2.3