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;
#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);
}
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)));
// 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);
}
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
// 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.
@ -1184,7 +1184,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
// Detailed error printed inside LockDataDirectory
return false;
}
return true;
}
bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif

View File

@ -27,27 +27,33 @@ void InitLogging();
void InitParameterInteraction();
/** 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.
*/
bool AppInitBasicSetup();
/**
* 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.
*/
bool AppInitParameterInteraction();
/**
* 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.
*/
bool AppInitSanityChecks();
/**
* Bitcoin core main initialization.
* @note This should only be done after daemonization.
* Lock bitcoin core data directory.
* @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.
*/
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);
/** The help message mode determines what help message to show */

View File

@ -178,6 +178,10 @@ class BitcoinCore: public QObject
Q_OBJECT
public:
explicit BitcoinCore();
/** Basic initialization, before starting initialization/shutdown thread.
* Return true on success.
*/
static bool baseInitialize();
public Q_SLOTS:
void initialize();
@ -270,26 +274,32 @@ void BitcoinCore::handleRunawayException(const std::exception *e)
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()
{
try
{
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);
Q_EMIT initializeResult(rv);
} catch (const std::exception& e) {
@ -689,9 +699,14 @@ int main(int argc, char *argv[])
if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
int rv = EXIT_SUCCESS;
try
{
app.createWindow(networkStyle.data());
// 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
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId());
@ -699,6 +714,11 @@ int main(int argc, char *argv[])
app.exec();
app.requestShutdown();
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) {
PrintExceptionContinue(&e, "Runaway exception");
app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
@ -706,6 +726,6 @@ int main(int argc, char *argv[])
PrintExceptionContinue(NULL, "Runaway exception");
app.handleRunawayException(QString::fromStdString(GetWarnings("gui")));
}
return app.getReturnValue();
return rv;
}
#endif // BITCOIN_QT_TEST