aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/qt/guiutil.h12
-rw-r--r--src/qt/walletcontroller.cpp17
2 files changed, 25 insertions, 4 deletions
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index c976b4b4bb..d7bd124884 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -340,6 +340,18 @@ namespace GUIUtil
#endif
}
+ /**
+ * Queue a function to run in an object's event loop. This can be
+ * replaced by a call to the QMetaObject::invokeMethod functor overload after Qt 5.10, but
+ * for now use a QObject::connect for compatibility with older Qt versions, based on
+ * https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style
+ */
+ template <typename Fn>
+ void ObjectInvoke(QObject* object, Fn&& function, Qt::ConnectionType connection = Qt::QueuedConnection)
+ {
+ QObject source;
+ QObject::connect(&source, &QObject::destroyed, object, std::forward<Fn>(function), connection);
+ }
} // namespace GUIUtil
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index d9e0274d01..83f3cccbff 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -128,10 +128,20 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
}
// Instantiate model and register it.
- WalletModel* wallet_model = new WalletModel(std::move(wallet), m_client_model, m_platform_style, nullptr);
- // Handler callback runs in a different thread so fix wallet model thread affinity.
+ WalletModel* wallet_model = new WalletModel(std::move(wallet), m_client_model, m_platform_style,
+ nullptr /* required for the following moveToThread() call */);
+
+ // Move WalletModel object to the thread that created the WalletController
+ // object (GUI main thread), instead of the current thread, which could be
+ // an outside wallet thread or RPC thread sending a LoadWallet notification.
+ // This ensures queued signals sent to the WalletModel object will be
+ // handled on the GUI event loop.
wallet_model->moveToThread(thread());
- wallet_model->setParent(this);
+ // setParent(parent) must be called in the thread which created the parent object. More details in #18948.
+ GUIUtil::ObjectInvoke(this, [wallet_model, this] {
+ wallet_model->setParent(this);
+ }, GUIUtil::blockingGUIThreadConnection());
+
m_wallets.push_back(wallet_model);
// WalletModel::startPollBalance needs to be called in a thread managed by
@@ -157,7 +167,6 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
// Re-emit coinsSent signal from wallet model.
connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent);
- // Notify walletAdded signal on the GUI thread.
Q_EMIT walletAdded(wallet_model);
return wallet_model;