0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-03-06 14:19:59 -05:00

qt: Add GUIUtil::ExceptionSafeConnect function

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 GUIUtil::ExceptionSafeConnect function should be used for
exception handling within slots.
This commit is contained in:
Hennadii Stepanov 2021-03-27 18:52:22 +02:00
parent 64a8755af3
commit f7e260a471
No known key found for this signature in database
GPG key ID: 410108112E7EA81F
2 changed files with 63 additions and 0 deletions

View file

@ -902,4 +902,15 @@ QString MakeHtmlLink(const QString& source, const QString& link)
QLatin1String("<a href=\"") % link % QLatin1String("\">") % link % QLatin1String("</a>")); QLatin1String("<a href=\"") % link % QLatin1String("\">") % link % QLatin1String("</a>"));
} }
void PrintSlotException(
const std::exception* exception,
const QObject* sender,
const QObject* receiver)
{
std::string description = sender->metaObject()->className();
description += "->";
description += receiver->metaObject()->className();
PrintExceptionContinue(exception, description.c_str());
}
} // namespace GUIUtil } // namespace GUIUtil

View file

@ -9,18 +9,23 @@
#include <fs.h> #include <fs.h>
#include <net.h> #include <net.h>
#include <netaddress.h> #include <netaddress.h>
#include <util/check.h>
#include <QApplication>
#include <QEvent> #include <QEvent>
#include <QHeaderView> #include <QHeaderView>
#include <QItemDelegate> #include <QItemDelegate>
#include <QLabel> #include <QLabel>
#include <QMessageBox> #include <QMessageBox>
#include <QMetaObject>
#include <QObject> #include <QObject>
#include <QProgressBar> #include <QProgressBar>
#include <QString> #include <QString>
#include <QTableView> #include <QTableView>
#include <cassert>
#include <chrono> #include <chrono>
#include <utility>
class QValidatedLineEdit; class QValidatedLineEdit;
class SendCoinsRecipient; class SendCoinsRecipient;
@ -332,6 +337,53 @@ namespace GUIUtil
*/ */
QString MakeHtmlLink(const QString& source, const QString& link); QString MakeHtmlLink(const QString& source, const QString& link);
void PrintSlotException(
const std::exception* exception,
const QObject* sender,
const QObject* receiver);
/**
* A drop-in replacement of QObject::connect function
* (see: https://doc.qt.io/qt-5/qobject.html#connect-3), that
* guaranties that all exceptions are handled within the slot.
*
* NOTE: This function is incompatible with Qt private signals.
*/
template <typename Sender, typename Signal, typename Receiver, typename Slot>
auto ExceptionSafeConnect(
Sender sender, Signal signal, Receiver receiver, Slot method,
Qt::ConnectionType type = Qt::AutoConnection)
{
return QObject::connect(
sender, signal, receiver,
[sender, receiver, method](auto&&... args) {
bool ok{true};
try {
(receiver->*method)(std::forward<decltype(args)>(args)...);
} catch (const NonFatalCheckError& e) {
PrintSlotException(&e, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleNonFatalException",
blockingGUIThreadConnection(),
Q_ARG(QString, QString::fromStdString(e.what())));
} catch (const std::exception& e) {
PrintSlotException(&e, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleRunawayException",
blockingGUIThreadConnection(),
Q_ARG(QString, QString::fromStdString(e.what())));
} catch (...) {
PrintSlotException(nullptr, sender, receiver);
ok = QMetaObject::invokeMethod(
qApp, "handleRunawayException",
blockingGUIThreadConnection(),
Q_ARG(QString, "Unknown failure occurred."));
}
assert(ok);
},
type);
}
} // namespace GUIUtil } // namespace GUIUtil
#endif // BITCOIN_QT_GUIUTIL_H #endif // BITCOIN_QT_GUIUTIL_H