New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
qt: Handle exceptions instead of crash #18897
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
Do I understand correctly that every gui element needs to have this handler if a crash wants to be avoided? I.e. every dialog or window that could possibly pop up and just putting this catch in the "main gui" won't work? |
Concept ACK either way |
Every slot. See: https://doc.qt.io/qt-5/exceptionsafety.html#signals-and-slots |
So currently it is undefined behaviour. Wow, qt is scary. Slightly stronger concept ACK, but this also makes we want to remove qt. |
Unrelated, but do we have a long-term plan of moving from Qt? |
Updated 40a622d -> 44f7f51 (pr18897.01 -> pr18897.02, diff):
The OP and the screenshot have been updated. |
This looks pretty reasonable, and I'd ack this if not a draft PR. One thing that might be nice is a generic exception catcher QObject that emits a handleRunawayException signal. This way the signal doesn't need to be declared multiple places and repeated boilerplate for catching exceptions could go away. So something like: m_exception_catcher->exec([&]{ sendButtonClickedHelper(); }); could replace: try {
sendButtonClickedHelper();
} catch (const std::exception& e) {
Q_EMIT runawayException(QString::fromStdString(e.what()));
} catch (...) {
Q_EMIT runawayException(QString("Unknown failure occurred."));
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's this approach:
bool BitcoinApplication::notify(QObject* receiver, QEvent* event)
{
try {
return QApplication::notify(receiver, event);
} catch (const std::exception& ex) {
// ..
} catch (...) {
// ..
}
return false;
}
But the documentation says this will be deprecated.
So currently it is undefined behaviour. Wow, qt is scary.
Slightly stronger concept ACK, but this also makes we want to remove qt.
Why? This is by design, Qt doesn't want to handle exceptions, what should it do anyway?
7c8178b
to
24a90d5
Compare
@ryanofsky @promag Thank you for your reviews. Updated 44f7f51 -> 24a90d5 (pr18897.02 -> pr18897.03, diff). Ready for review now :) |
Updated 24a90d5 -> 82529bc (pr18897.03 -> pr18897.04, diff):
|
Also #18435 could be related. |
Rebased 82529bc -> 1fb68b3 (pr18897.04 -> pr18897.05) due to the conflict with #18914. |
Concept ACK |
Throwing an exception from a slot invoked by Qt's signal-slot connection mechanism is considered undefined behavior, unless it is handled within the slot. The shieldException() function should be used for exception handling within slots.
Updated 1fb68b3 -> 8f94243 (pr18897.05 -> pr18897.06, diff):
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review ACK 8f94243. This looks very good and much simpler than before!
IMO it would be nice to have a followup PR that eliminated the one-line forwarding methods and just used shieldException directly at the call and signal connect sites. Alternately renaming s/Helper/Unsafe/ would be clearer than status quo. But best to just get rid of the indirections.
bool ok = true; | ||
try { | ||
f(); | ||
} catch (const NonFatalCheckError& e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "qt: Add shieldException() function" (553128b)
Would drop this special case for NonFatalCheckError and simply catch std::exception below. NonFatalCheckError is really an internal implementation of the CHECK macro and most useful to unit tests. Exceptions aren't valid in qt callbacks so NonFatalCheckError is no different here than any other exception. Just another unexpected condition that should never happen
PrintExceptionContinue(&e, "Internal error"); | ||
ok = QMetaObject::invokeMethod(qApp, "handleNonFatalException", blockingGUIThreadConnection(), Q_ARG(QString, QString::fromStdString(e.what()))); | ||
} catch (const std::exception& e) { | ||
PrintExceptionContinue(&e, "Runaway exception"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "qt: Add shieldException() function" (553128b)
Here and below would replace "Runaway exception" with "Internal error." No reason to confuse users with implementation details and obscure jargon when it's not providing useful information
🐙 This pull request conflicts with the target branch and needs rebase. |
Reopening this not to lose track, since it doesn't appear this PR was moved to the GUI repo (or I can't find it). Can close this again if it is actually moved or no longer applicable. |
@hebasto can you either move this over to the GUI repo, or close and we'll mark it up for grabs? The interested re-opener can open it in the gui repo. |
|
b8e5d0d qt: Handle exceptions in SendCoinsDialog::sendButtonClicked slot (Hennadii Stepanov) 1ac2bc7 qt: Handle exceptions in TransactionView::bumpFee slot (Hennadii Stepanov) bc00e13 qt: Handle exceptions in WalletModel::pollBalanceChanged slot (Hennadii Stepanov) eb6156b qt: Handle exceptions in BitcoinGUI::addWallet slot (Hennadii Stepanov) f7e260a qt: Add GUIUtil::ExceptionSafeConnect function (Hennadii Stepanov) 64a8755 qt: Add BitcoinApplication::handleNonFatalException function (Hennadii Stepanov) af7e365 qt: Make PACKAGE_BUGREPORT link clickable (Hennadii Stepanov) Pull request description: This PR is an alternative to bitcoin/bitcoin#18897, and is based on Russ' [idea](bitcoin/bitcoin#18897 (review)): > IMO it would be nice to have a followup PR that eliminated the one-line forwarding methods ... Related issues - #91 - bitcoin/bitcoin#18643 Qt docs: https://doc.qt.io/qt-5.12/exceptionsafety.html#exceptions-in-client-code With this PR the GUI handles the wallet-related exception, and: - display it to a user: ![Screenshot from 2021-04-01 02-55-59](https://user-images.githubusercontent.com/32963518/113226183-33ff8480-9298-11eb-8fe6-2168834ab09a.png) - prints a message to `stderr`: ``` ************************ EXCEPTION: 18NonFatalCheckError wallet/wallet.cpp:2677 (IsCurrentForAntiFeeSniping) Internal bug detected: '!chain.findBlock(block_hash, FoundBlock().time(block_time))' You may report this issue here: https://github.com/bitcoin/bitcoin/issues bitcoin in QPushButton->SendCoinsDialog ``` - writes a message to the `debug.log` - and, if the exception is a non-fatal error, leaves the main window running. ACKs for top commit: laanwj: Code review ACK b8e5d0d ryanofsky: Code review ACK b8e5d0d. This is great! I think more improvements are possible but implementation is very clean and I love how targeted each commit is. Changes since last review: adding more explanatory text, making links clickable, reorganizing. Tree-SHA512: a9f2a2ee8e64b993b0dbc454edcbc39c68c8852abb5dc1feb58f601c0e0e8014dca81c72733aa3fb07b619c6f49b823ed20c7d79cc92088a3abe040ed2149727
Related issue #18643.
With this PR the GUI handles the wallet-related exception, displays it to a user:
and, if the exception is a non-fatal error, leaves the main window running.