init: Factor out AppInitLockDataDirectory

Alternative to #10818, alternative solution to #10815.

After this change: All the AppInit steps before and inclusive
AppInitLockDataDirectory must not have Shutdown() called in case of
failure. Only when AppInitMain fails, Shutdown should be called.

Changes the GUI and bitcoind code to consistently do this.
This commit is contained in:
Wladimir J. van der Laan 2017-07-15 10:46:06 +02:00
parent 8fdd23a224
commit dba485d651
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
4 changed files with 67 additions and 30 deletions

View file

@ -159,7 +159,12 @@ bool AppInit(int argc, char* argv[])
return false; return false;
#endif // HAVE_DECL_DAEMON #endif // HAVE_DECL_DAEMON
} }
// Lock data directory after daemonization
if (!AppInitLockDataDirectory())
{
// If locking the data directory failed, exit immediately
exit(EXIT_FAILURE);
}
fRet = AppInitMain(threadGroup, scheduler); fRet = AppInitMain(threadGroup, scheduler);
} }
catch (const std::exception& e) { catch (const std::exception& e) {

View file

@ -1170,13 +1170,13 @@ bool AppInitSanityChecks()
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME))); return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), _(PACKAGE_NAME)));
// Probe the data directory lock to give an early error message, if possible // Probe the data directory lock to give an early error message, if possible
// We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened,
// and a fork will cause weird behavior to it.
return LockDataDirectory(true); return LockDataDirectory(true);
} }
bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) bool AppInitLockDataDirectory()
{ {
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
// After daemonization get the data directory lock again and hold on to it until exit // After daemonization get the data directory lock again and hold on to it until exit
// This creates a slight window for a race condition to happen, however this condition is harmless: it // This creates a slight window for a race condition to happen, however this condition is harmless: it
// will at most make us exit without printing a message to console. // will at most make us exit without printing a message to console.
@ -1184,7 +1184,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// Detailed error printed inside LockDataDirectory // Detailed error printed inside LockDataDirectory
return false; return false;
} }
return true;
}
bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
#ifndef WIN32 #ifndef WIN32
CreatePidFile(GetPidFile(), getpid()); CreatePidFile(GetPidFile(), getpid());
#endif #endif

View file

@ -27,27 +27,33 @@ void InitLogging();
void InitParameterInteraction(); void InitParameterInteraction();
/** Initialize bitcoin core: Basic context setup. /** Initialize bitcoin core: Basic context setup.
* @note This can be done before daemonization. * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read. * @pre Parameters should be parsed and config file should be read.
*/ */
bool AppInitBasicSetup(); bool AppInitBasicSetup();
/** /**
* Initialization: parameter interaction. * Initialization: parameter interaction.
* @note This can be done before daemonization. * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called. * @pre Parameters should be parsed and config file should be read, AppInitBasicSetup should have been called.
*/ */
bool AppInitParameterInteraction(); bool AppInitParameterInteraction();
/** /**
* Initialization sanity checks: ecc init, sanity checks, dir lock. * Initialization sanity checks: ecc init, sanity checks, dir lock.
* @note This can be done before daemonization. * @note This can be done before daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called. * @pre Parameters should be parsed and config file should be read, AppInitParameterInteraction should have been called.
*/ */
bool AppInitSanityChecks(); bool AppInitSanityChecks();
/** /**
* Bitcoin core main initialization. * Lock bitcoin core data directory.
* @note This should only be done after daemonization. * @note This should only be done after daemonization. Do not call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitSanityChecks should have been called. * @pre Parameters should be parsed and config file should be read, AppInitSanityChecks should have been called.
*/ */
bool AppInitLockDataDirectory();
/**
* Bitcoin core main initialization.
* @note This should only be done after daemonization. Call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
*/
bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler); bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler);
/** The help message mode determines what help message to show */ /** The help message mode determines what help message to show */

View file

@ -178,6 +178,10 @@ class BitcoinCore: public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit BitcoinCore(); explicit BitcoinCore();
/** Basic initialization, before starting initialization/shutdown thread.
* Return true on success.
*/
static bool baseInitialize();
public Q_SLOTS: public Q_SLOTS:
void initialize(); void initialize();
@ -270,26 +274,32 @@ void BitcoinCore::handleRunawayException(const std::exception *e)
Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui"))); Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui")));
} }
bool BitcoinCore::baseInitialize()
{
if (!AppInitBasicSetup())
{
return false;
}
if (!AppInitParameterInteraction())
{
return false;
}
if (!AppInitSanityChecks())
{
return false;
}
if (!AppInitLockDataDirectory())
{
return false;
}
return true;
}
void BitcoinCore::initialize() void BitcoinCore::initialize()
{ {
try try
{ {
qDebug() << __func__ << ": Running initialization in thread"; qDebug() << __func__ << ": Running initialization in thread";
if (!AppInitBasicSetup())
{
Q_EMIT initializeResult(false);
return;
}
if (!AppInitParameterInteraction())
{
Q_EMIT initializeResult(false);
return;
}
if (!AppInitSanityChecks())
{
Q_EMIT initializeResult(false);
return;
}
bool rv = AppInitMain(threadGroup, scheduler); bool rv = AppInitMain(threadGroup, scheduler);
Q_EMIT initializeResult(rv); Q_EMIT initializeResult(rv);
} catch (const std::exception& e) { } catch (const std::exception& e) {
@ -689,16 +699,26 @@ int main(int argc, char *argv[])
if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false)) if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data()); app.createSplashScreen(networkStyle.data());
int rv = EXIT_SUCCESS;
try try
{ {
app.createWindow(networkStyle.data()); app.createWindow(networkStyle.data());
app.requestInitialize(); // Perform base initialization before spinning up initialization/shutdown thread
// This is acceptable because this function only contains steps that are quick to execute,
// so the GUI thread won't be held up.
if (BitcoinCore::baseInitialize()) {
app.requestInitialize();
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId()); WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
#endif #endif
app.exec(); app.exec();
app.requestShutdown(); app.requestShutdown();
app.exec(); app.exec();
rv = app.getReturnValue();
} else {
// A dialog with detailed error will have been shown by InitError()
rv = EXIT_FAILURE;
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
PrintExceptionContinue(&e, "Runaway exception"); PrintExceptionContinue(&e, "Runaway exception");
app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
@ -706,6 +726,6 @@ int main(int argc, char *argv[])
PrintExceptionContinue(NULL, "Runaway exception"); PrintExceptionContinue(NULL, "Runaway exception");
app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
} }
return app.getReturnValue(); return rv;
} }
#endif // BITCOIN_QT_TEST #endif // BITCOIN_QT_TEST