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:
- 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.
- 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:
-
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.
-
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);
- 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.