GUI unresponsive during slow operations #10504

issue ryanofsky openend this issue on June 1, 2017
  1. ryanofsky commented at 6:32 pm on June 1, 2017: contributor

    As mentioned https://botbot.me/freenode/bitcoin-core-dev/msg/83983170/, in Qt making slow or blocking calls in the event loop thread makes the user interface nonresponsive. Keyboard and mouse events are not processed and the display is not updated. There are different places in the code where this can happen, so this github issue is intended to be an umbrella issue to figure out a general strategy for turning synchronous calls into asynchronous ones and dealing with the problem.

    Background

    In Qt, there are two different ways to keep the event thread responsive when handling an event that triggers an asynchronous operation:

    1. Return from the event handler right away after starting the operation (usually in some worker thread) so the main event loop can keep processing events.
    2. Run a nested event loop in the event handling function in parallel with the asynchronous operation.

    Approach 1 requires using a using continuation style of programming that works best when an event only needs to trigger a single operation and UI update.

    Approach 2 can be more convenient when handling events that do more than trigger a single slow operation followed by a UI update. It can be a lot easier to implement than approach 1 in existing code, where separating all the slow operations from the ui updates could require rewriting control flow.

    Approach 2 doesn’t work well when the user interface needs to support multiple async operations running in parallel. (For example if there are two buttons that both trigger async operations with nested event loops followed by UI updates, and you click the first button then the second button before the first operation completes, then the first ui update will be blocked until after the second nested loop exits even if the first operation completes first.)

    Strategies for improvement

    Given choices between returning to the main event loop or using nested loops, different strategies are possible for making slow synchronous operations asynchronous and making the GUI more responsive:

    1. Rewriting all blocking operations in the event thread in continuation style, never blocking in any event handler, and always returning immediately to the main event loop. This is pretty easy to do in new code, but would require rewriting a lot of existing code.

    2. Making a helper function using a nested event loop and a worker thread that would allow blocking code in event handlers to be easily replaced by asynchronous code, without having to change control flow. E.g. a DoAsync function that allows mechanically replacing code like:

    0ResultType result = SomeSlowOperation();
    1widget->updateState(result);
    

    with:

    0ResultType result = DoAsync([&]() { return SomeSlowOperation(); });
    1widget->updateState(result);
    
    1. Running nested event loops as part of the framework introduced by #10244, using the approach described in #10102 (comment). This would allow code in Qt that appears to be blocking just transparently handle events in the background.

    Regardless of which of the above strategies are used, it could make sense to use the #10244 refactoring as a starting point because it makes blocking calls in Qt code easy to identify (they all happen through m_node / m_wallet accesses), and in a few places it combines scattered operations into single calls.

  2. jonasschnelli added the label Brainstorming on Jun 1, 2017
  3. jonasschnelli added the label GUI on Jun 1, 2017
  4. ryanofsky commented at 11:08 am on June 16, 2017: contributor
    IRC discussion yesterday: https://botbot.me/freenode/bitcoin-core-dev/msg/87282208/. Jonas mentioned later there is blocking calling sendtoaddress after generate: https://botbot.me/freenode/bitcoin-core-dev/msg/87296576/. And there was just a bug report of ui lag during syncing on windows: https://github.com/bitcoin/bitcoin/issues/10610
  5. ryanofsky commented at 11:17 am on June 16, 2017: contributor

    This stackoverflow question: https://stackoverflow.com/questions/15097861/qt-measure-event-handling-time has sample code for measuring time it takes to process Qt events. We could adopt it and log events that take longer than a certain amount of time to be processed to get a better idea of where there are and aren’t problems with blocking.

    Another more sophisticated approach might be to add a new thread that monitors the event thread and dumps a stack trace of the event thread when it doesn’t return from a handler fast enough.

  6. ryanofsky commented at 9:42 pm on December 7, 2017: contributor
    Some more talk about switching to async operations in GUI here: https://botbot.me/freenode/bitcoin-core-dev/msg/94385934/
  7. laanwj referenced this in commit 5f0c6a7b0e on Apr 5, 2018
  8. cryptozeny referenced this in commit b6f0a9d6c1 on Feb 8, 2019
  9. cryptozeny referenced this in commit 2a96f22c92 on Feb 8, 2019
  10. cryptozeny referenced this in commit 50403a93c6 on Feb 8, 2019
  11. cryptozeny referenced this in commit ea78562840 on Feb 8, 2019
  12. cryptozeny referenced this in commit fff78b9b6f on Feb 8, 2019
  13. cryptozeny referenced this in commit 72436c90b2 on Feb 8, 2019
  14. promag commented at 3:17 pm on October 15, 2019: member
    I think the only valid approach is run blocking code in a different thread.
  15. promag commented at 3:43 pm on October 15, 2019: member
    @ryanofsky I mean that nested even loops are pointless here. Do you think otherwise?
  16. ryanofsky commented at 4:02 pm on October 15, 2019: contributor

    nested even loops are pointless here. Do you think otherwise?

    Not if they make the gui more responsive and can be implemented with no changes to existing GUI code as described. In general I tend to think it’s better if GUI just to respond to events and make asynchronous requests, but we’ve had this discussion before (https://github.com/bitcoin/bitcoin/pull/15153#discussion_r249851732). It’s all good.

  17. laanwj commented at 8:02 pm on October 15, 2019: member
    Though they can be useful, one thing to be careful with with nested event loops: in the past we had an issue where a pointer to a stack-local object from the inner loop stayed around after escaping to the global event loop, inside Qt, through some very unexpected mechanism. I can’t find the issue but see e.g. http://jblog.andbit.net/2006/10/24/nested-eventloops/
  18. promag commented at 10:28 pm on October 15, 2019: member
    @laanwj are you referring to #15310?
  19. laanwj commented at 10:15 am on October 16, 2019: member
    @promag something much longer ago, but I guess that one’s similar.
  20. kord1e commented at 8:40 pm on February 2, 2022: none
    Latest git revision, it’s so unresponsive during intensive operations that it quickly becomes annoying. Improvements would be appreciated.
  21. pinheadmz closed this on Apr 27, 2023

  22. bitcoin locked this on Apr 26, 2024

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-05 19:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me