diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -82,6 +82,8 @@ // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool *) Q_DECLARE_METATYPE(CAmount) +// Config is non-copyable so we can only register pointers to it +Q_DECLARE_METATYPE(Config *) static void InitMessage(const std::string &message) { LogPrintf("init message: %s\n", message); @@ -180,7 +182,7 @@ explicit BitcoinCore(); public Q_SLOTS: - void initialize(); + void initialize(Config *config); void shutdown(); Q_SIGNALS: @@ -217,9 +219,9 @@ void createSplashScreen(const NetworkStyle *networkStyle); /// Request core initialization - void requestInitialize(); + void requestInitialize(Config &config); /// Request core shutdown - void requestShutdown(); + void requestShutdown(Config &config); /// Get process return value int getReturnValue() { return returnValue; } @@ -235,7 +237,7 @@ void handleRunawayException(const QString &message); Q_SIGNALS: - void requestedInitialize(); + void requestedInitialize(Config *config); void requestedShutdown(); void stopThread(); void splashFinished(QWidget *window); @@ -266,7 +268,8 @@ Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui"))); } -void BitcoinCore::initialize() { +void BitcoinCore::initialize(Config *cfg) { + Config &config(*cfg); try { qDebug() << __func__ << ": Running AppInit2 in thread"; if (!AppInitBasicSetup()) { @@ -274,10 +277,6 @@ return; } - // FIXME: Ideally, we'd like to build the config here, but that's - // currently not possible as the whole application has too many global - // state. However, this is a first step. - auto &config = const_cast<Config &>(GetConfig()); if (!AppInitParameterInteraction(config)) { Q_EMIT initializeResult(false); return; @@ -396,7 +395,23 @@ SLOT(shutdownResult(int))); connect(executor, SIGNAL(runawayException(QString)), this, SLOT(handleRunawayException(QString))); - connect(this, SIGNAL(requestedInitialize()), executor, SLOT(initialize())); + + // Note on how Qt works: it tries to directly invoke methods if the signal + // is emitted on the same thread that the target object 'lives' on. + // But if the target object 'lives' on another thread (executor here does) + // the SLOT will be invoked asynchronously at a later time in the thread + // of the target object. So.. we pass a pointer around. If you pass + // a reference around (even if it's non-const) you'll get Qt generating + // code to copy-construct the parameter in question (Q_DECLARE_METATYPE + // and qRegisterMetaType generate this code). For the Config class, + // which is noncopyable, we can't do this. So.. we have to pass + // pointers to Config around. Make sure Config &/Config * isn't a + // temporary (eg it lives somewhere aside from the stack) or this will + // crash because initialize() gets executed in another thread at some + // unspecified time (after) requestedInitialize() is emitted! + connect(this, SIGNAL(requestedInitialize(Config *)), executor, + SLOT(initialize(Config *))); + connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown())); /* make sure executor object is deleted in its own thread */ connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater())); @@ -410,13 +425,16 @@ InitParameterInteraction(); } -void BitcoinApplication::requestInitialize() { +void BitcoinApplication::requestInitialize(Config &config) { qDebug() << __func__ << ": Requesting initialize"; startThread(); - Q_EMIT requestedInitialize(); + // IMPORTANT: config must NOT be a reference to a temporary because below + // signal may be connected to a slot that will be executed as a queued + // connection in another thread! + Q_EMIT requestedInitialize(&config); } -void BitcoinApplication::requestShutdown() { +void BitcoinApplication::requestShutdown(Config &config) { // Show a simple window indicating shutdown status. Do this first as some of // the steps may take some time below, for example the RPC console may still // be executing a command. @@ -568,6 +586,14 @@ // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType<CAmount>("CAmount"); + // Need to register any types Qt doesn't know about if you intend + // to use them with the signal/slot mechanism Qt provides. Even pointers. + // Note that class Config is noncopyable and so we can't register a + // non-pointer version of it with Qt, because Qt expects to be able to + // copy-construct non-pointers to objects for invoking slots + // behind-the-scenes in the 'Queued' connection case. + qRegisterMetaType<Config *>(); + /// 3. Application identification // must be set before OptionsModel is initialized or translations are // loaded, as it is used to locate QSettings. @@ -691,13 +717,16 @@ // Subscribe to global signals from core uiInterface.InitMessage.connect(InitMessage); + // Get global config + Config &config = const_cast<Config &>(GetConfig()); + if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); try { app.createWindow(networkStyle.data()); - app.requestInitialize(); + app.requestInitialize(config); #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 WinShutdownMonitor::registerShutdownBlockReason( QObject::tr("%1 didn't yet exit safely...") @@ -705,7 +734,7 @@ (HWND)app.getMainWinId()); #endif app.exec(); - app.requestShutdown(); + app.requestShutdown(config); app.exec(); } catch (const std::exception &e) { PrintExceptionContinue(&e, "Runaway exception");