Skip to content

Conversation

promag
Copy link
Contributor

@promag promag commented Oct 14, 2019

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.

@jonasschnelli
Copy link
Contributor

Elegant Solution.
Concept ACK.
In the long run, we should probably also improve the 250ms polling on WalletModel::pollBalanceChanged()

Will test.

@promag
Copy link
Contributor Author

promag commented Oct 15, 2019

also improve the 250ms polling

You mean remove polling? :trollface:

@promag promag force-pushed the 2019-10-fix-gui-freeze branch from c5487d7 to d3dd119 Compare October 15, 2019 14:24
@promag promag changed the title wip: gui: It's freezing gui: Make polling in ClientModel asynchronous Oct 15, 2019
@laanwj
Copy link
Member

laanwj commented Oct 17, 2019

Concept ACK

@maflcko
Copy link
Member

maflcko commented Oct 17, 2019

Put in a gitian build, but I no longer have a windows box for testing

Copy link
Member

@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 8f0d8bd

Didn't test on windows to confirm this actually reduces the "not responding" pop ups, nor that the mempool size is still updated

@DrahtBot
Copy link
Contributor

Gitian builds for commit ec3ed5a (master):

Gitian builds for commit 6661358 (master and this pull):

@Sjors
Copy link
Member

Sjors commented Oct 21, 2019

I can confirm that 8f0d8bd makes the unresponsive behavior go away on macOS 10.15 Catalina (e.g. the file menu now works at launch).

@promag promag force-pushed the 2019-10-fix-gui-freeze branch from 8f0d8bd to 8cf1c67 Compare October 21, 2019 17:26
@promag
Copy link
Contributor Author

promag commented Oct 21, 2019

@MarcoFalke @Sjors force push with

--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -57,9 +57,9 @@ ClientModel::~ClientModel()
 {
     unsubscribeFromCoreSignals();

-    delete pollTimer;
     m_thread->quit();
     m_thread->wait();
+    delete pollTimer;
 }

 int ClientModel::getNumConnections(unsigned int flags) const

@luke-jr
Copy link
Member

luke-jr commented Oct 21, 2019

"Moved to another thread" isn't really "asynchronous", but Concept ACK

// move timer to thread so that polling doesn't disturb event loop
pollTimer->moveToThread(m_thread);
m_thread->start();
connect(pollTimer, &QTimer::timeout, [this] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you leave ClientModel::updateTimer as-is for now? Or at least move the code movement to another commit?

Copy link
Contributor Author

@promag promag Oct 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but I'd have to connect with Qt::DirectConnection, otherwise the slot would be called in the GUI event loop.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even after moveToThread? Because of the slot being on a GUI object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, with auto connection (the default) the signal checks if the target's thread is the same as the sender or not, if not then it's the same as queued connection.

Copy link
Member

@laanwj laanwj Oct 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should do this connect before moving the timer to the thread?
(or maybe not, I'm not sure here…)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QObject::connect is thread safe.

pollTimer = new QTimer(this);
connect(pollTimer, &QTimer::timeout, this, &ClientModel::updateTimer);

pollTimer = new QTimer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't this use QTimer(m_thread)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why can't this be a unique_ptr?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it deleted children when being deleted itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't have a tree of objects with different thread affinity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why can't this be a unique_ptr?

It can, but it requires more changes, which are unnecessary in the scope of this PR.

@promag
Copy link
Contributor Author

promag commented Oct 21, 2019

@luke-jr right, conceptually asynchronous from the GUI thread.

@fjahr
Copy link
Contributor

fjahr commented Oct 24, 2019

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.


m_thread->quit();
m_thread->wait();
delete pollTimer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

connect(&thread, &QThread::finished, pollTimer, &QTimer::deleteLater);

(like in RPCConsole::startExecutor())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

With this change polling runs in a different thread to prevent
disturbing the event loop.
@promag promag force-pushed the 2019-10-fix-gui-freeze branch from 092d91f to 6b6be41 Compare October 25, 2019 13:54
promag added a commit to promag/bitcoin that referenced this pull request Oct 25, 2019
With this change polling runs in a different thread to prevent
disturbing the event loop.

Github-Pull: bitcoin#17135
Rebased-From: 6b6be41
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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ops, I think this is wrong - deleteLater isn't called because the thread's event loop is not running. I'll check this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

@laanwj laanwj Oct 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phew, good to know (I've read that once but forgot)

@laanwj
Copy link
Member

laanwj commented Oct 25, 2019

ACK 6b6be41

@Sjors
Copy link
Member

Sjors commented Oct 25, 2019

Code review re-ACK 6b6be41; only replaced the scary cast with { timer->start(); }

laanwj added a commit that referenced this pull request Oct 26, 2019
6b6be41 gui: Make polling in ClientModel asynchronous (João Barbosa)

Pull request description:

  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.

ACKs for top commit:
  laanwj:
    ACK 6b6be41
  Sjors:
    Code review re-ACK 6b6be41; only replaced the scary cast with `{ timer->start(); }`

Tree-SHA512: fd98b0c6535441aee3ee03c48b58b4b1f9bdd172ec6b8150da883022f719df34cabfd4c133412bf410e7f709f7bf1e9ef16dca05ef1f3689d526ceaeee51de38
@laanwj laanwj merged commit 6b6be41 into bitcoin:master Oct 26, 2019
laanwj added a commit that referenced this pull request Oct 26, 2019
d5c36ce gui: Make polling in ClientModel asynchronous (João Barbosa)

Pull request description:

  Backport #17135.

ACKs for top commit:
  laanwj:
    ACK d5c36ce, it is a clean cherry-pick of 6b6be41.

Tree-SHA512: 4e514f205866d87bdc19a57dede2214891237d7b663c9c8c9f19a9ab5c5a6e64876065bebb6c16a1799b02e0eb971318866b4e0824155b47063ce379fb0155e2
@promag promag deleted the 2019-10-fix-gui-freeze branch October 26, 2019 10:06
HashUnlimited pushed a commit to HashUnlimited/chaincoin that referenced this pull request Nov 17, 2019
With this change polling runs in a different thread to prevent
disturbing the event loop.

Github-Pull: bitcoin#17135
Rebased-From: 6b6be41
fxtc pushed a commit to fxtc/fxtc that referenced this pull request Nov 25, 2019
With this change polling runs in a different thread to prevent
disturbing the event loop.

Github-Pull: bitcoin#17135
Rebased-From: 6b6be41
fxtc pushed a commit to fxtc/fxtc that referenced this pull request Nov 25, 2019
With this change polling runs in a different thread to prevent
disturbing the event loop.

Github-Pull: bitcoin#17135
Rebased-From: 6b6be41
laanwj added a commit that referenced this pull request Mar 31, 2020
…ceChanged

0933a37 gui: Avoid Wallet::GetBalance in WalletModel::pollBalanceChanged (João Barbosa)

Pull request description:

  Each 250ms the slot `WalletModel::pollBalanceChanged` is called which, at worst case, calls `Wallet::GetBalance`. This is a waste of resources since most of the time there aren't new transactions or new blocks. Fix this by early checking if cache is dirty or not.

  The actual balance computation can still hang the GUI thread but that is tracked in #16874 and should be fixed with a solution similar to #17135.

ACKs for top commit:
  hebasto:
    ACK 0933a37, I have not tested the code, but I have reviewed it and it looks OK, I agree it can be merged.
  jonasschnelli:
    utACK 0933a37
  instagibbs:
    ACK 0933a37
  ryanofsky:
    Code review ACK 0933a37, but I would prefer (not strongly) for #17905 to be merged first. This PR can be simpler if it is based on #17905, so tryGetBalances can just be left alone instead of changing into to a more complicated tryGetBalancesIfNeeded function, and then getting changed back later when we want to optimize it out.
  jonatack:
    ACK 0933a37 based primarily on code review, despite a lot of manual testing with a large 177MB wallet.

Tree-SHA512: 18db35bf33a7577666658c8cb0b57308c8474baa5ea95bf1468cd8531a69857d8915584f6ac505874717aa6aabeb1b506ac77630f8acdb6651afab89275e38a1
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Mar 31, 2020
…llBalanceChanged

0933a37 gui: Avoid Wallet::GetBalance in WalletModel::pollBalanceChanged (João Barbosa)

Pull request description:

  Each 250ms the slot `WalletModel::pollBalanceChanged` is called which, at worst case, calls `Wallet::GetBalance`. This is a waste of resources since most of the time there aren't new transactions or new blocks. Fix this by early checking if cache is dirty or not.

  The actual balance computation can still hang the GUI thread but that is tracked in bitcoin#16874 and should be fixed with a solution similar to bitcoin#17135.

ACKs for top commit:
  hebasto:
    ACK 0933a37, I have not tested the code, but I have reviewed it and it looks OK, I agree it can be merged.
  jonasschnelli:
    utACK 0933a37
  instagibbs:
    ACK 0933a37
  ryanofsky:
    Code review ACK 0933a37, but I would prefer (not strongly) for bitcoin#17905 to be merged first. This PR can be simpler if it is based on bitcoin#17905, so tryGetBalances can just be left alone instead of changing into to a more complicated tryGetBalancesIfNeeded function, and then getting changed back later when we want to optimize it out.
  jonatack:
    ACK 0933a37 based primarily on code review, despite a lot of manual testing with a large 177MB wallet.

Tree-SHA512: 18db35bf33a7577666658c8cb0b57308c8474baa5ea95bf1468cd8531a69857d8915584f6ac505874717aa6aabeb1b506ac77630f8acdb6651afab89275e38a1
deadalnix pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Oct 31, 2020
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
kwvg added a commit to kwvg/dash that referenced this pull request Nov 1, 2021
kwvg added a commit to kwvg/dash that referenced this pull request Nov 1, 2021
pravblockc pushed a commit to pravblockc/dash that referenced this pull request Nov 18, 2021
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants