aboutsummaryrefslogtreecommitdiff
path: root/src/qt
diff options
context:
space:
mode:
authorHennadii Stepanov <32963518+hebasto@users.noreply.github.com>2021-03-27 18:52:22 +0200
committerHennadii Stepanov <32963518+hebasto@users.noreply.github.com>2021-04-01 03:05:31 +0300
commitf7e260a471010e2d656fbc5ea8c310f6d94c26b9 (patch)
tree83d89d077fd4345daafa491e7bf8ad4420e2245f /src/qt
parent64a8755af396f1c2791018510e22b58114e68594 (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.cpp11
-rw-r--r--src/qt/guiutil.h52
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