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.
Elegant Solution.
Concept ACK.
In the long run, we should probably also improve the 250ms polling on WalletModel::pollBalanceChanged()
Will test.
also improve the 250ms polling
You mean remove polling? :trollface:
89@@ -90,7 +90,9 @@ class ClientModel : public QObject
90 PeerTableModel *peerTableModel;
91 BanTableModel *banTableModel;
92
93- QTimer *pollTimer;
94+ //! A thread to interact with m_node asynchronously
95+ QThread* const thread;
0 QThread* const m_thread;
:eyes: ?
108-{
109- // no locking required at this point
110- // the following calls will acquire the required lock
111- Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
112- Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
113-}
I could keep this here but the connection from the timer needs to be direct (not auto) so that this would run in the timer’s thread.
Also, this is now unreachable, and it’s a small step towards removing polling.
ACK 8f0d8bd17a575a4235296d82cda80ba268a01dba
Didn’t test on windows to confirm this actually reduces the “not responding” pop ups, nor that the mempool size is still updated
Gitian builds for commit ec3ed5a4487886f1c2a35fda0a3289be7b280248 (master):
f64e5b9561bbfd24763ca216dfb72e6c...
bitcoin-0.19.99-aarch64-linux-gnu-debug.tar.gz3450cd39da524d4259d8971fa07f7cd3...
bitcoin-0.19.99-aarch64-linux-gnu.tar.gzf9c08aecb54c510abc1c49a0f615cc47...
bitcoin-0.19.99-arm-linux-gnueabihf-debug.tar.gz48858aa869f72b05f6fd77a174cec72d...
bitcoin-0.19.99-arm-linux-gnueabihf.tar.gz2f3d551fa3a0744ced807e575f1e4643...
bitcoin-0.19.99-i686-pc-linux-gnu-debug.tar.gz61eef7a7416e41bd49f11481339df776...
bitcoin-0.19.99-i686-pc-linux-gnu.tar.gzbefb99d795c38a2f320e99e677e437fb...
bitcoin-0.19.99-osx-unsigned.dmgaadf3d3811d300b416a2e3a36e5d9636...
bitcoin-0.19.99-osx64.tar.gz75ad9c5b79ef283885def690f4210e28...
bitcoin-0.19.99-riscv64-linux-gnu-debug.tar.gz0bdc56205b7a686505b49d9694dfbb7d...
bitcoin-0.19.99-riscv64-linux-gnu.tar.gz0ed997185c1a65c6ab29882306a5c4ed...
bitcoin-0.19.99-win64-debug.zipe642bb08c080af778aad7ab1bc889769...
bitcoin-0.19.99-win64-setup-unsigned.exe471525f13e4ee98129b3e140175776a5...
bitcoin-0.19.99-win64.zipa2d38074e8a186c6251fbb30948caf6f...
bitcoin-0.19.99-x86_64-linux-gnu-debug.tar.gz544d75a68b31657850b22296599a3d3f...
bitcoin-0.19.99-x86_64-linux-gnu.tar.gzc780c4b108f18250dfaa13a06d7ded80...
bitcoin-0.19.99.tar.gz42923b2fe3a4f72ea620650c3be0a040...
bitcoin-core-linux-0.20-res.yml15e9a3c42739179b937c4bb927c02731...
bitcoin-core-osx-0.20-res.yml4f45465e66f847f97cfd6af3c2bd3514...
bitcoin-core-win-0.20-res.ymla34a94c2dee07af19dfd80a405f39c1d...
linux-build.loge0a214ba922d8febd8566d7102414cc0...
osx-build.logbfb37291238fa4be8727e7c882d422e0...
win-build.logGitian builds for commit 6661358a0c082fd50378fe6fc88dddbd0566ebf0 (master and this pull):
bd75e5285ba3454089eea66851951a3e...
bitcoin-0.19.99-aarch64-linux-gnu-debug.tar.gz847ad3dbf8e9f1c9b9d3826d534258a6...
bitcoin-0.19.99-aarch64-linux-gnu.tar.gzabc3a5164207550aeaf56721f32edd2c...
bitcoin-0.19.99-arm-linux-gnueabihf-debug.tar.gz45bb13636a019270312cba6197ffd32c...
bitcoin-0.19.99-arm-linux-gnueabihf.tar.gzfb0a6b56c92ac730da300429a600b9db...
bitcoin-0.19.99-i686-pc-linux-gnu-debug.tar.gz0bd7258f275d4d34ad07d4ec905b1b3f...
bitcoin-0.19.99-i686-pc-linux-gnu.tar.gz4d9ca8b60fc979544cb99ffc4e232484...
bitcoin-0.19.99-osx-unsigned.dmgbdfe572383c66989ca8a463ed1ee48af...
bitcoin-0.19.99-osx64.tar.gz7b986c3b3cf8a6b3c7cbbe5d442a27dc...
bitcoin-0.19.99-riscv64-linux-gnu-debug.tar.gzf5bb3c29e722f8e092d4660dc168d777...
bitcoin-0.19.99-riscv64-linux-gnu.tar.gzfd1b3e6297f2e86cd2df663c3f6d6f92...
bitcoin-0.19.99-win64-debug.zip773470fe260bc71e17da117b37c5e9ff...
bitcoin-0.19.99-win64-setup-unsigned.exe7945027d5a1e6ffb14e07cb2b5816dab...
bitcoin-0.19.99-win64.zip00c4bc060177f9a8969740538cb9c9b2...
bitcoin-0.19.99-x86_64-linux-gnu-debug.tar.gz3945984613ad6cdd1d229cc1e63af38f...
bitcoin-0.19.99-x86_64-linux-gnu.tar.gz8cc01845714d8ad70ff969ca8d518d02...
bitcoin-0.19.99.tar.gzffee7001f20c1c3ff9c3502c2e00f692...
bitcoin-core-linux-0.20-res.yml37d12c597b73b9f31a3aa1ff1c9b4c4f...
bitcoin-core-linux-0.20-res.yml.difff0a73b15111d52d814864e7dbb376e9a...
bitcoin-core-osx-0.20-res.yml3381d609d590467fa206e35d2cf70081...
bitcoin-core-osx-0.20-res.yml.diffeb691454dfca0bc139a483c4a51bd49a...
bitcoin-core-win-0.20-res.ymle56f284e2044226f0d5e96a2c2ac2f3b...
bitcoin-core-win-0.20-res.yml.diffb9636440f209ed05e6cc33613d049c82...
linux-build.log3de26d2981536c352e0e25d6a7d6a066...
linux-build.log.diff4c64ce4e05da72a2d5c6bf6a737d76b5...
osx-build.logf761b312fafeaa275c9c53073d68cce3...
osx-build.log.diff695226feb3f04e98b30265d3f29f1dc9...
win-build.log41ce19eae944a02dac666d0c41f5d6e3...
win-build.log.diff55@@ -46,6 +56,10 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
56 ClientModel::~ClientModel()
57 {
58 unsubscribeFromCoreSignals();
59+
60+ delete pollTimer;
@MarcoFalke @Sjors force push with
0--- a/src/qt/clientmodel.cpp
1+++ b/src/qt/clientmodel.cpp
2@@ -57,9 +57,9 @@ ClientModel::~ClientModel()
3 {
4 unsubscribeFromCoreSignals();
5
6- delete pollTimer;
7 m_thread->quit();
8 m_thread->wait();
9+ delete pollTimer;
10 }
11
12 int ClientModel::getNumConnections(unsigned int flags) const
43+
44+ pollTimer = new QTimer;
45+ // move timer to thread so that polling doesn't disturb event loop
46+ pollTimer->moveToThread(m_thread);
47+ m_thread->start();
48+ connect(pollTimer, &QTimer::timeout, [this] {
ClientModel::updateTimer
as-is for now? Or at least move the code movement to another commit?
moveToThread
? Because of the slot being on a GUI object?
QObject::connect
is thread safe.
39 peerTableModel = new PeerTableModel(m_node, this);
40 banTableModel = new BanTableModel(m_node, this);
41- pollTimer = new QTimer(this);
42- connect(pollTimer, &QTimer::timeout, this, &ClientModel::updateTimer);
43+
44+ pollTimer = new QTimer;
QTimer(m_thread)
?
Because Qt threading model :trollface:
Being a child of QThread does nothing other than be owned by it. To have it’s events processed in the thread then moveToThread must be used. This also works only if the object being moved has no parent.
why can’t this be a unique_ptr?
It can, but it requires more changes, which are unnecessary in the scope of this PR.
tested ACK 8f0d8bd
Built, ran tests, some manual testing while running for 24h on macOS.
Please add a description. I got the context from the core dev logs but there should be some information here as well.
55@@ -46,6 +56,10 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
56 ClientModel::~ClientModel()
57 {
58 unsubscribeFromCoreSignals();
59+
60+ m_thread->quit();
61+ m_thread->wait();
62+ delete pollTimer;
As discussed on IRC: I don’t think deleting the timer in the main thread, after having moved it to another thread is safe. Qt objects need to be deleted in the thread that owns them. I think that that thread doesn’t exist anymore here doesn’t change that. You’re not even supposed to hold on to the pointer.
I think you can do this before moving it to the thread to make sure it’s cleaned up:
0connect(&thread, &QThread::finished, pollTimer, &QTimer::deleteLater);
(like in RPCConsole::startExecutor()
)
49+ // the following calls will acquire the required lock
50+ Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
51+ Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
52+ });
53+ connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
54+ connect(m_thread, &QThread::started, timer, static_cast<void (QTimer::*)()>(&QTimer::start));
QTimer::start
is overloaded.
could this work instead of the scary cast ?
0connect(m_thread, &QThread::started, timer, [timer] {
1 timer->start();
2});
Yes that works, and the following also works:
0connect(m_thread, &QThread::started, [timer] {
1 timer->start();
2});
because started
is emitted from the associated thread.
ACK 092d91f6196af497b9618418f84527f552ef1154. Tested without a wallet (bitcoin-qt -nowallet
) by calling invalidateblock
and later reconsiderblock
. Without this commit QT is very unresponsive during sync. The original issue seems less pronounced with a wallet.
Would it make sense to create a NodePoller object, which handles the timer life cycle? Or is that overkill if the plan is to get rid of polling asap anyway?
Would it make sense to create a NodePoller object, which handles the timer life cycle? Or is that overkill if the plan is to get rid of polling asap anyway?
I think that as long as polling is necessary (and I don’t believe we will completely get rid of it any time soon) it would make sense to have one thread to do all polling, and not, say, create a thread per WalletModel too.
But it’s out of scope for this PR.
With this change polling runs in a different thread to prevent
disturbing the event loop.
48+ // no locking required at this point
49+ // the following calls will acquire the required lock
50+ Q_EMIT mempoolSizeChanged(m_node.getMempoolSize(), m_node.getMempoolDynamicUsage());
51+ Q_EMIT bytesChanged(m_node.getTotalBytesRecv(), m_node.getTotalBytesSent());
52+ });
53+ connect(m_thread, &QThread::finished, timer, &QObject::deleteLater);
deleteLater
isn’t called because the thread’s event loop is not running. I’ll check this.
Nope, all good! From https://doc.qt.io/qt-5/qthread.html#finished:
When this signal is emitted, the event loop has already stopped running. No more events will be processed in the thread, except for deferred deletion events. This signal can be connected to QObject::deleteLater(), to free objects in that thread.
{ timer->start(); }
promag
jonasschnelli
MarcoFalke
laanwj
DrahtBot
ysangkok
Sjors
luke-jr
fjahr
Labels
GUI
Milestone
0.19.0