diff options
author | Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> | 2021-03-27 18:52:22 +0200 |
---|---|---|
committer | Hennadii Stepanov <32963518+hebasto@users.noreply.github.com> | 2021-04-01 03:05:31 +0300 |
commit | f7e260a471010e2d656fbc5ea8c310f6d94c26b9 (patch) | |
tree | 83d89d077fd4345daafa491e7bf8ad4420e2245f /src/qt | |
parent | 64a8755af396f1c2791018510e22b58114e68594 (diff) |
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.
Diffstat (limited to 'src/qt')
-rw-r--r-- | src/qt/guiutil.cpp | 11 | ||||
-rw-r--r-- | src/qt/guiutil.h | 52 |
2 files changed, 63 insertions, 0 deletions
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 36fcc4d361..0e91f9f385 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -902,4 +902,15 @@ QString MakeHtmlLink(const QString& source, const QString& link) 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 diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 6ab0a71a96..a1cf274354 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -9,18 +9,23 @@ #include <fs.h> #include <net.h> #include <netaddress.h> +#include <util/check.h> +#include <QApplication> #include <QEvent> #include <QHeaderView> #include <QItemDelegate> #include <QLabel> #include <QMessageBox> +#include <QMetaObject> #include <QObject> #include <QProgressBar> #include <QString> #include <QTableView> +#include <cassert> #include <chrono> +#include <utility> class QValidatedLineEdit; class SendCoinsRecipient; @@ -332,6 +337,53 @@ namespace GUIUtil */ 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 #endif // BITCOIN_QT_GUIUTIL_H |