From b31499ec72edd1554d4612d1b54808fce0360e14 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Sat, 9 Mar 2013 12:02:57 -0500 Subject: Clean up shutdown process --- src/init.cpp | 193 ++++++++++++++++++++++++++++------------------------------- 1 file changed, 93 insertions(+), 100 deletions(-) (limited to 'src/init.cpp') diff --git a/src/init.cpp b/src/init.cpp index a2e85f2ef2..3ccd01e3bc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -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) + if (!fRet) { + if (detectShutdownThread) + detectShutdownThread->interrupt(); + threadGroup.interrupt_all(); + } + + if (detectShutdownThread) { - Shutdown(NULL); - threadGroup.interrupt_all(); - threadGroup.join_all(); + 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= " + _("Listen for JSON-RPC connections on (default: 8332 or testnet: 18332)") + "\n" + " -rpcallowip= " + _("Allow JSON-RPC connections from specified IP address") + "\n" + " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n" + - " -rpcthreads= " + _("Use this mean threads to service RPC calls (default: 4)") + "\n" + + " -rpcthreads= " + _("Use this many threads to service RPC calls (default: 4)") + "\n" + " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + " -walletnotify= " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + " -alertnotify= " + _("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; } -- cgit v1.2.3