Skip to content

Commit

Permalink
gui: Make polling in ClientModel asynchronous
Browse files Browse the repository at this point in the history
Summary:
> After #14193 ClientModel::updateTimer can take some time, as such the GUI hangs, like #17112.
>
> Fixes this by polling in a background thread and updating the GUI asynchronously.

This is a backport of Core [[bitcoin/bitcoin#17135 | PR17135]] and Core [[bitcoin/bitcoin#17427 | PR17427]]

The second PR fixes a bug introduced by the first (`ClientModel::mempoolSizeChanged() signal has unregistered parameter type size_t`)

Test Plan: `ninja && ninja check && src/qt/bitcoin-qt`

Reviewers: O1 Bitcoin ABC, #bitcoin_abc, Fabien

Reviewed By: O1 Bitcoin ABC, #bitcoin_abc, Fabien

Subscribers: Fabien

Differential Revision: https://reviews.bitcoinabc.org/D8193
  • Loading branch information
promag authored and PiRK committed Oct 31, 2020
1 parent 9acf260 commit 3bfb5fc
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 18 deletions.
12 changes: 8 additions & 4 deletions src/qt/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,15 +541,19 @@ int GuiMain(int argc, char *argv[]) {

BitcoinApplication app(*node);

// Register meta types used for QMetaObject::invokeMethod
// Register meta types used for QMetaObject::invokeMethod and
// Qt::QueuedConnection
qRegisterMetaType<bool *>();
#ifdef ENABLE_WALLET
qRegisterMetaType<WalletModel *>();
#endif
// Need to pass name here as Amount is a typedef (see
// http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
// IMPORTANT if it is no longer a typedef use the normal variant above
// Register typedefs (see
// http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
// IMPORTANT: if Amount is no longer a typedef use the normal variant above
// (see https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1)
qRegisterMetaType<Amount>("Amount");
qRegisterMetaType<size_t>("size_t");

qRegisterMetaType<std::function<void()>>("std::function<void()>");
qRegisterMetaType<QMessageBox::Icon>("QMessageBox::Icon");

Expand Down
34 changes: 22 additions & 12 deletions src/qt/clientmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <util/system.h>

#include <QDebug>
#include <QThread>
#include <QTimer>

#include <cstdint>
Expand All @@ -28,20 +29,37 @@ static int64_t nLastBlockTipUpdateNotification = 0;
ClientModel::ClientModel(interfaces::Node &node, OptionsModel *_optionsModel,
QObject *parent)
: QObject(parent), m_node(node), optionsModel(_optionsModel),
peerTableModel(nullptr), banTableModel(nullptr), pollTimer(nullptr) {
peerTableModel(nullptr), banTableModel(nullptr),
m_thread(new QThread(this)) {
cachedBestHeaderHeight = -1;
cachedBestHeaderTime = -1;
peerTableModel = new PeerTableModel(m_node, this);
banTableModel = new BanTableModel(m_node, this);
pollTimer = new QTimer(this);
connect(pollTimer, &QTimer::timeout, this, &ClientModel::updateTimer);
pollTimer->start(MODEL_UPDATE_DELAY);

QTimer *timer = new QTimer;
timer->setInterval(MODEL_UPDATE_DELAY);
connect(timer, &QTimer::timeout, [this] {
// no locking required at this point
// the following calls will acquire the required lock
Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(),
m_node.getMempoolDynamicUsage());
Q_EMIT bytesChanged(m_node.getTotalBytesRecv(),
m_node.getTotalBytesSent());
});
connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
connect(m_thread, &QThread::started, [timer] { timer->start(); });
// move timer to thread so that polling doesn't disturb main event loop
timer->moveToThread(m_thread);
m_thread->start();

subscribeToCoreSignals();
}

ClientModel::~ClientModel() {
unsubscribeFromCoreSignals();

m_thread->quit();
m_thread->wait();
}

int ClientModel::getNumConnections(NumConnections flags) const {
Expand Down Expand Up @@ -84,14 +102,6 @@ int64_t ClientModel::getHeaderTipTime() const {
return cachedBestHeaderTime;
}

void ClientModel::updateTimer() {
// no locking required at this point
// the following calls will acquire the required lock
Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(),
m_node.getMempoolDynamicUsage());
Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
}

void ClientModel::updateNumConnections(int numConnections) {
Q_EMIT numConnectionsChanged(numConnections);
}
Expand Down
4 changes: 2 additions & 2 deletions src/qt/clientmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ class ClientModel : public QObject {
PeerTableModel *peerTableModel;
BanTableModel *banTableModel;

QTimer *pollTimer;
//! A thread to interact with m_node asynchronously
QThread *const m_thread;

void subscribeToCoreSignals();
void unsubscribeFromCoreSignals();
Expand All @@ -109,7 +110,6 @@ class ClientModel : public QObject {
void showProgress(const QString &title, int nProgress);

public Q_SLOTS:
void updateTimer();
void updateNumConnections(int numConnections);
void updateNetworkActive(bool networkActive);
void updateAlert();
Expand Down

0 comments on commit 3bfb5fc

Please sign in to comment.