Clean up shutdown process

This commit is contained in:
Gavin Andresen
2013-03-09 12:02:57 -05:00
parent 21eb5adadb
commit b31499ec72
15 changed files with 147 additions and 178 deletions

View File

@ -40,75 +40,65 @@ enum BindFlags {
// Shutdown
//
void ExitTimeout(void* parg)
{
#ifdef WIN32
MilliSleep(5000);
ExitProcess(0);
#endif
}
//
// Thread management and startup/shutdown:
//
// The network-processing threads are all part of a thread group
// created by AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM
// signal handler sets fRequestShutdown, which triggers
// the DetectShutdownThread(), which interrupts the main thread group.
// DetectShutdownThread() then exits, which causes AppInit() to
// continue (it .joins the shutdown thread).
// Shutdown() is then
// called to clean up database connections, and stop other
// threads that should only be stopped after the main network-processing
// threads have exited.
//
// Note that if running -daemon the parent process returns from AppInit2
// before adding any threads to the threadGroup, so .join_all() returns
// immediately and the parent exits from main().
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
// fRequestShutdown getting set (either by RPC stop or SIGTERM)
// and then does the normal Qt shutdown thing.
//
volatile bool fRequestShutdown = false;
void StartShutdown()
{
#ifdef QT_GUI
// ensure we leave the Qt main loop for a clean GUI exit (Shutdown() is called in bitcoin.cpp afterwards)
uiInterface.QueueShutdown();
#else
// Without UI, Shutdown() can simply be started in a new thread
NewThread(Shutdown, NULL);
#endif
fRequestShutdown = true;
}
static CCoinsViewDB *pcoinsdbview;
void Shutdown(void* parg)
void Shutdown()
{
static CCriticalSection cs_Shutdown;
static bool fTaken;
TRY_LOCK(cs_Shutdown, lockShutdown);
if (!lockShutdown) return;
// Make this thread recognisable as the shutdown thread
RenameThread("bitcoin-shutoff");
nTransactionsUpdated++;
StopRPCThreads();
bitdb.Flush(false);
StopNode();
{
fShutdown = true;
fRequestShutdown = true;
nTransactionsUpdated++;
StopRPCThreads();
bitdb.Flush(false);
StopNode();
{
LOCK(cs_main);
if (pblocktree)
pblocktree->Flush();
if (pcoinsTip)
pcoinsTip->Flush();
delete pcoinsTip;
delete pcoinsdbview;
delete pblocktree;
}
bitdb.Flush(true);
boost::filesystem::remove(GetPidFile());
UnregisterWallet(pwalletMain);
delete pwalletMain;
NewThread(ExitTimeout, NULL);
MilliSleep(50);
printf("Bitcoin exited\n\n");
fExit = true;
#ifndef QT_GUI
// ensure non-UI client gets exited here, but let Bitcoin-Qt reach 'return 0;' in bitcoin.cpp
exit(0);
#endif
}
else
{
while (!fExit)
MilliSleep(500);
MilliSleep(100);
ExitThread(0);
LOCK(cs_main);
if (pblocktree)
pblocktree->Flush();
if (pcoinsTip)
pcoinsTip->Flush();
delete pcoinsTip; pcoinsTip = NULL;
delete pcoinsdbview; pcoinsdbview = NULL;
delete pblocktree; pblocktree = NULL;
}
bitdb.Flush(true);
boost::filesystem::remove(GetPidFile());
UnregisterWallet(pwalletMain);
delete pwalletMain;
}
//
@ -116,9 +106,13 @@ void Shutdown(void* parg)
//
void DetectShutdownThread(boost::thread_group* threadGroup)
{
while (fRequestShutdown == false)
// Tell the main threads to shutdown.
while (!fRequestShutdown)
{
MilliSleep(200);
threadGroup->interrupt_all();
if (fRequestShutdown)
threadGroup->interrupt_all();
}
}
void HandleSIGTERM(int)
@ -143,6 +137,8 @@ void HandleSIGHUP(int)
bool AppInit(int argc, char* argv[])
{
boost::thread_group threadGroup;
boost::thread* detectShutdownThread = NULL;
bool fRet = false;
try
{
@ -154,7 +150,7 @@ bool AppInit(int argc, char* argv[])
if (!boost::filesystem::is_directory(GetDataDir(false)))
{
fprintf(stderr, "Error: Specified directory does not exist\n");
Shutdown(NULL);
Shutdown();
}
ReadConfigFile(mapArgs, mapMultiArgs);
@ -184,7 +180,31 @@ bool AppInit(int argc, char* argv[])
int ret = CommandLineRPC(argc, argv);
exit(ret);
}
#if !defined(WIN32)
fDaemon = GetBoolArg("-daemon");
if (fDaemon)
{
// Daemonize
pid_t pid = fork();
if (pid < 0)
{
fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
return false;
}
if (pid > 0) // Parent process, pid is child process id
{
CreatePidFile(GetPidFile(), pid);
return true;
}
// Child process falls through to rest of initialization
pid_t sid = setsid();
if (sid < 0)
fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
}
#endif
detectShutdownThread = new boost::thread(boost::bind(&DetectShutdownThread, &threadGroup));
fRet = AppInit2(threadGroup);
}
catch (std::exception& e) {
@ -192,12 +212,20 @@ bool AppInit(int argc, char* argv[])
} catch (...) {
PrintExceptionContinue(NULL, "AppInit()");
}
if (!fRet)
{
Shutdown(NULL);
threadGroup.interrupt_all();
threadGroup.join_all();
if (!fRet) {
if (detectShutdownThread)
detectShutdownThread->interrupt();
threadGroup.interrupt_all();
}
if (detectShutdownThread)
{
detectShutdownThread->join();
delete detectShutdownThread;
detectShutdownThread = NULL;
}
Shutdown();
return fRet;
}
@ -214,7 +242,7 @@ int main(int argc, char* argv[])
if (fRet && fDaemon)
return 0;
return 1;
return (fRet ? 0 : 1);
}
#endif
@ -302,7 +330,7 @@ std::string HelpMessage()
" -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n" +
" -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n" +
" -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" +
" -rpcthreads=<n> " + _("Use this mean threads to service RPC calls (default: 4)") + "\n" +
" -rpcthreads=<n> " + _("Use this many threads to service RPC calls (default: 4)") + "\n" +
" -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
" -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" +
" -alertnotify=<cmd> " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" +
@ -440,8 +468,6 @@ bool AppInit2(boost::thread_group& threadGroup)
sigaction(SIGHUP, &sa_hup, NULL);
#endif
threadGroup.create_thread(boost::bind(&DetectShutdownThread, &threadGroup));
// ********************************************************* Step 2: parameter interactions
fTestNet = GetBoolArg("-testnet");
@ -499,12 +525,6 @@ bool AppInit2(boost::thread_group& threadGroup)
else
fDebugNet = GetBoolArg("-debugnet");
#if !defined(WIN32) && !defined(QT_GUI)
fDaemon = GetBoolArg("-daemon");
#else
fDaemon = false;
#endif
if (fDaemon)
fServer = true;
else
@ -552,28 +572,6 @@ bool AppInit2(boost::thread_group& threadGroup)
if (!lock.try_lock())
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str()));
#if !defined(WIN32) && !defined(QT_GUI)
if (fDaemon)
{
// Daemonize
pid_t pid = fork();
if (pid < 0)
{
fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
return false;
}
if (pid > 0)
{
CreatePidFile(GetPidFile(), pid);
return true;
}
pid_t sid = setsid();
if (sid < 0)
fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
}
#endif
if (GetBoolArg("-shrinkdebugfile", !fDebug))
ShrinkDebugFile();
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
@ -1011,8 +1009,7 @@ bool AppInit2(boost::thread_group& threadGroup)
printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size());
printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size());
if (!NewThread(StartNode, (void*)&threadGroup))
InitError(_("Error: could not start node"));
StartNode(threadGroup);
if (fServer)
StartRPCThreads();
@ -1030,12 +1027,8 @@ bool AppInit2(boost::thread_group& threadGroup)
// Add wallet transactions that aren't already in a block to mapTransactions
pwalletMain->ReacceptWalletTransactions();
#if !defined(QT_GUI)
// Loop until process is exit()ed from shutdown() function,
// called from ThreadRPCServer thread when a "stop" command is received.
while (1)
MilliSleep(5000);
#endif
// Run a thread to flush wallet periodically
threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile)));
return true;
return !fRequestShutdown;
}