Wallet: rework wallet refreshing

This commit is contained in:
xiphon 2020-10-09 12:04:53 +00:00
parent d3943ca2a9
commit 39d9d7d071
5 changed files with 100 additions and 45 deletions

View file

@ -1895,7 +1895,7 @@ ApplicationWindow {
repeat: true repeat: true
running: persistentSettings.autosave running: persistentSettings.autosave
onTriggered: { onTriggered: {
if (currentWallet) { if (currentWallet && !currentWallet.refreshing) {
currentWallet.storeAsync(function(success) { currentWallet.storeAsync(function(success) {
if (success) { if (success) {
appWindow.showStatusMessage(qsTr("Autosaved the wallet"), 3); appWindow.showStatusMessage(qsTr("Autosaved the wallet"), 3);

View file

@ -27,6 +27,11 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Wallet.h" #include "Wallet.h"
#include <chrono>
#include <stdexcept>
#include <thread>
#include "PendingTransaction.h" #include "PendingTransaction.h"
#include "UnsignedTransaction.h" #include "UnsignedTransaction.h"
#include "TransactionHistory.h" #include "TransactionHistory.h"
@ -50,6 +55,8 @@
#include <QVector> #include <QVector>
#include <QMutexLocker> #include <QMutexLocker>
#include "qt/ScopeGuard.h"
namespace { namespace {
static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 5; static const int DAEMON_BLOCKCHAIN_HEIGHT_CACHE_TTL_SECONDS = 5;
static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30; static const int DAEMON_BLOCKCHAIN_TARGET_HEIGHT_CACHE_TTL_SECONDS = 30;
@ -124,6 +131,19 @@ bool Wallet::disconnected() const
return m_disconnected; 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) void Wallet::setConnectionStatus(ConnectionStatus value)
{ {
if (m_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( const auto future = m_scheduler.run(
[this, path] { [this, path] {
QMutexLocker locker(&m_storeMutex); QMutexLocker locker(&m_asyncMutex);
return QJSValueList({m_walletImpl->store(path.toStdString())}); return QJSValueList({m_walletImpl->store(path.toStdString())});
}, },
@ -263,7 +283,7 @@ void Wallet::initAsync(
emit walletCreationHeightChanged(); emit walletCreationHeightChanged();
qDebug() << "init async finished - starting refresh"; qDebug() << "init async finished - starting refresh";
connected(true); connected(true);
m_walletImpl->startRefresh(); startRefresh();
} }
}); });
if (future.first) if (future.first)
@ -466,41 +486,37 @@ bool Wallet::importKeyImages(const QString& path)
return m_walletImpl->importKeyImages(path.toStdString()); return m_walletImpl->importKeyImages(path.toStdString());
} }
bool Wallet::refresh() bool Wallet::refresh(bool historyAndSubaddresses /* = true */)
{ {
bool result = m_walletImpl->refresh(); refreshingSet(true);
m_history->refresh(currentSubaddressAccount()); const auto cleanup = sg::make_scope_guard([this]() noexcept {
m_subaddress->refresh(currentSubaddressAccount()); refreshingSet(false);
m_subaddressAccount->getAll(); });
if (result)
emit updated(); {
return result; 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_refreshEnabled = true;
m_walletImpl->refreshAsync();
} }
void Wallet::setAutoRefreshInterval(int seconds) void Wallet::pauseRefresh()
{ {
m_walletImpl->setAutoRefreshInterval(seconds); m_refreshEnabled = false;
}
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, 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() bool Wallet::rescanSpent()
{ {
QMutexLocker locker(&m_asyncMutex);
return m_walletImpl->rescanSpent(); return m_walletImpl->rescanSpent();
} }
@ -1041,6 +1059,8 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
, m_subaddressModel(nullptr) , m_subaddressModel(nullptr)
, m_subaddressAccount(nullptr) , m_subaddressAccount(nullptr)
, m_subaddressAccountModel(nullptr) , m_subaddressAccountModel(nullptr)
, m_refreshEnabled(false)
, m_refreshing(false)
, m_scheduler(this) , m_scheduler(this)
{ {
m_history = new TransactionHistory(m_walletImpl->history(), this); m_history = new TransactionHistory(m_walletImpl->history(), this);
@ -1058,6 +1078,8 @@ Wallet::Wallet(Monero::Wallet *w, QObject *parent)
m_connectionStatusRunning = false; m_connectionStatusRunning = false;
m_daemonUsername = ""; m_daemonUsername = "";
m_daemonPassword = ""; m_daemonPassword = "";
startRefreshThread();
} }
Wallet::~Wallet() Wallet::~Wallet()
@ -1090,3 +1112,32 @@ Wallet::~Wallet()
m_walletListener = NULL; m_walletListener = NULL;
qDebug("m_walletImpl deleted"); 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<std::chrono::seconds>(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");
}
}

View file

@ -63,6 +63,7 @@ class Wallet : public QObject, public PassprasePrompter
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged) 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 seed READ getSeed)
Q_PROPERTY(QString seedLanguage READ getSeedLanguage) Q_PROPERTY(QString seedLanguage READ getSeedLanguage)
Q_PROPERTY(Status status READ status) Q_PROPERTY(Status status READ status)
@ -207,20 +208,11 @@ public:
Q_INVOKABLE bool importKeyImages(const QString& path); Q_INVOKABLE bool importKeyImages(const QString& path);
//! refreshes the wallet //! refreshes the wallet
Q_INVOKABLE bool refresh(); Q_INVOKABLE bool refresh(bool historyAndSubaddresses = true);
//! 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;
// pause/resume refresh // pause/resume refresh
Q_INVOKABLE void startRefresh() const; Q_INVOKABLE void startRefresh();
Q_INVOKABLE void pauseRefresh() const; Q_INVOKABLE void pauseRefresh();
//! creates transaction //! creates transaction
Q_INVOKABLE PendingTransaction * createTransaction(const QString &dst_addr, const QString &payment_id, Q_INVOKABLE PendingTransaction * createTransaction(const QString &dst_addr, const QString &payment_id,
@ -394,6 +386,7 @@ signals:
void currentSubaddressAccountChanged() const; void currentSubaddressAccountChanged() const;
void disconnectedChanged() const; void disconnectedChanged() const;
void proxyAddressChanged() const; void proxyAddressChanged() const;
void refreshingChanged() const;
private: private:
Wallet(QObject * parent = nullptr); Wallet(QObject * parent = nullptr);
@ -421,9 +414,12 @@ private:
const QString& proxyAddress); const QString& proxyAddress);
bool disconnected() const; bool disconnected() const;
bool refreshing() const;
void refreshingSet(bool value);
void setConnectionStatus(ConnectionStatus value); void setConnectionStatus(ConnectionStatus value);
QString getProxyAddress() const; QString getProxyAddress() const;
void setProxyAddress(QString address); void setProxyAddress(QString address);
void startRefreshThread();
private: private:
friend class WalletManager; friend class WalletManager;
@ -454,15 +450,17 @@ private:
mutable SubaddressModel * m_subaddressModel; mutable SubaddressModel * m_subaddressModel;
SubaddressAccount * m_subaddressAccount; SubaddressAccount * m_subaddressAccount;
mutable SubaddressAccountModel * m_subaddressAccountModel; mutable SubaddressAccountModel * m_subaddressAccountModel;
QMutex m_asyncMutex;
QMutex m_connectionStatusMutex; QMutex m_connectionStatusMutex;
bool m_connectionStatusRunning; bool m_connectionStatusRunning;
QString m_daemonUsername; QString m_daemonUsername;
QString m_daemonPassword; QString m_daemonPassword;
QString m_proxyAddress; QString m_proxyAddress;
mutable QMutex m_proxyMutex; mutable QMutex m_proxyMutex;
std::atomic<bool> m_refreshEnabled;
std::atomic<bool> m_refreshing;
WalletListenerImpl *m_walletListener; WalletListenerImpl *m_walletListener;
FutureScheduler m_scheduler; FutureScheduler m_scheduler;
QMutex m_storeMutex;
}; };

View file

@ -65,6 +65,11 @@ QPair<bool, QFuture<QJSValueList>> FutureScheduler::run(std::function<QJSValueLi
}); });
} }
bool FutureScheduler::stopping() const noexcept
{
return Stopping;
}
bool FutureScheduler::add() noexcept bool FutureScheduler::add() noexcept
{ {
QMutexLocker locker(&Mutex); QMutexLocker locker(&Mutex);

View file

@ -23,6 +23,7 @@ public:
QPair<bool, QFuture<void>> run(std::function<void()> function) noexcept; QPair<bool, QFuture<void>> run(std::function<void()> function) noexcept;
QPair<bool, QFuture<QJSValueList>> run(std::function<QJSValueList()> function, const QJSValue &callback); QPair<bool, QFuture<QJSValueList>> run(std::function<QJSValueList()> function, const QJSValue &callback);
bool stopping() const noexcept;
private: private:
bool add() noexcept; bool add() noexcept;
@ -59,7 +60,7 @@ private:
size_t Alive; size_t Alive;
QWaitCondition Condition; QWaitCondition Condition;
QMutex Mutex; QMutex Mutex;
bool Stopping; std::atomic<bool> Stopping;
}; };
#endif // FUTURE_SCHEDULER_H #endif // FUTURE_SCHEDULER_H