From c8c2fbe07f1a5475aea3a2680af9130558c7e5c8 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Wed, 6 Mar 2013 22:16:05 -0500 Subject: Shutdown cleanup prep-work Create a boost::thread_group object at the qt/bitcoind main-loop level that will hold pointers to all the main-loop threads. This will replace the vnThreadsRunning[] array. For testing, ported the BitcoinMiner threads to use its own boost::thread_group. --- src/init.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'src/init.cpp') diff --git a/src/init.cpp b/src/init.cpp index aac46d489..a5015adc4 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -122,6 +122,16 @@ void Shutdown(void* parg) } } +// +// Signal handlers are very limited in what they are allowed to do, so: +// +void DetectShutdownThread(boost::thread_group* threadGroup) +{ + while (fRequestShutdown == false) + Sleep(200); + threadGroup->interrupt_all(); +} + void HandleSIGTERM(int) { fRequestShutdown = true; @@ -143,6 +153,7 @@ void HandleSIGHUP(int) #if !defined(QT_GUI) bool AppInit(int argc, char* argv[]) { + boost::thread_group threadGroup; bool fRet = false; try { @@ -185,7 +196,7 @@ bool AppInit(int argc, char* argv[]) exit(ret); } - fRet = AppInit2(); + fRet = AppInit2(threadGroup); } catch (std::exception& e) { PrintExceptionContinue(&e, "AppInit()"); @@ -193,7 +204,11 @@ bool AppInit(int argc, char* argv[]) PrintExceptionContinue(NULL, "AppInit()"); } if (!fRet) + { Shutdown(NULL); + threadGroup.interrupt_all(); + threadGroup.join_all(); + } return fRet; } @@ -405,7 +420,7 @@ void ThreadImport(void *data) { /** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. */ -bool AppInit2() +bool AppInit2(boost::thread_group& threadGroup) { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -449,6 +464,8 @@ bool AppInit2() sigaction(SIGHUP, &sa_hup, NULL); #endif + threadGroup.create_thread(boost::bind(&DetectShutdownThread, &threadGroup)); + // ********************************************************* Step 2: parameter interactions fTestNet = GetBoolArg("-testnet"); -- cgit v1.2.3 From 1b43bf0d3ae7b1fcde0c0e20c23c341540f4c8d2 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 7 Mar 2013 14:25:21 -0500 Subject: Rename util.h Sleep --> MilliSleep Two reasons for this change: 1. Need to always use boost::thread's sleep, even on Windows, so the sleeps can be interrupted (prior code used Windows' built-in Sleep). 2. I always forgot what units the old Sleep took. --- src/init.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/init.cpp') diff --git a/src/init.cpp b/src/init.cpp index a5015adc4..d61bfa892 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -43,7 +43,7 @@ enum BindFlags { void ExitTimeout(void* parg) { #ifdef WIN32 - Sleep(5000); + MilliSleep(5000); ExitProcess(0); #endif } @@ -105,7 +105,7 @@ void Shutdown(void* parg) UnregisterWallet(pwalletMain); delete pwalletMain; NewThread(ExitTimeout, NULL); - Sleep(50); + MilliSleep(50); printf("Bitcoin exited\n\n"); fExit = true; #ifndef QT_GUI @@ -116,8 +116,8 @@ void Shutdown(void* parg) else { while (!fExit) - Sleep(500); - Sleep(100); + MilliSleep(500); + MilliSleep(100); ExitThread(0); } } @@ -1061,7 +1061,7 @@ bool AppInit2(boost::thread_group& threadGroup) // Loop until process is exit()ed from shutdown() function, // called from ThreadRPCServer thread when a "stop" command is received. while (1) - Sleep(5000); + MilliSleep(5000); #endif return true; -- cgit v1.2.3 From 21eb5adadbe3110a8708f2570185566e1f137a49 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Wed, 6 Mar 2013 22:31:26 -0500 Subject: Port Thread* methods to boost::thread_group --- src/init.cpp | 73 +++++++++++++++++++----------------------------------------- 1 file changed, 23 insertions(+), 50 deletions(-) (limited to 'src/init.cpp') diff --git a/src/init.cpp b/src/init.cpp index d61bfa892..a2e85f2ef 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -68,27 +68,16 @@ void Shutdown(void* parg) // Make this thread recognisable as the shutdown thread RenameThread("bitcoin-shutoff"); - - bool fFirstThread = false; - { - TRY_LOCK(cs_Shutdown, lockShutdown); - if (lockShutdown) - { - fFirstThread = !fTaken; - fTaken = true; - } - } - static bool fExit; - if (fFirstThread) + nTransactionsUpdated++; + StopRPCThreads(); + bitdb.Flush(false); + StopNode(); { fShutdown = true; fRequestShutdown = true; nTransactionsUpdated++; + StopRPCThreads(); bitdb.Flush(false); - { - LOCK(cs_main); - ThreadScriptCheckQuit(); - } StopNode(); { LOCK(cs_main); @@ -128,7 +117,7 @@ void Shutdown(void* parg) void DetectShutdownThread(boost::thread_group* threadGroup) { while (fRequestShutdown == false) - Sleep(200); + MilliSleep(200); threadGroup->interrupt_all(); } @@ -313,6 +302,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" + " -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" + @@ -354,22 +344,16 @@ struct CImportingNow } }; -struct CImportData { - std::vector vFiles; -}; - -void ThreadImport(void *data) { - CImportData *import = reinterpret_cast(data); +void ThreadImport(std::vector vImportFiles) +{ RenameThread("bitcoin-loadblk"); - vnThreadsRunning[THREAD_IMPORT]++; - // -reindex if (fReindex) { CImportingNow imp; int nFile = 0; - while (!fRequestShutdown) { + while (true) { CDiskBlockPos pos(nFile, 0); FILE *file = OpenBlockFile(pos, true); if (!file) @@ -378,18 +362,16 @@ void ThreadImport(void *data) { LoadExternalBlockFile(file, &pos); nFile++; } - if (!fRequestShutdown) { - pblocktree->WriteReindexing(false); - fReindex = false; - printf("Reindexing finished\n"); - // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): - InitBlockIndex(); - } + pblocktree->WriteReindexing(false); + fReindex = false; + printf("Reindexing finished\n"); + // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): + InitBlockIndex(); } // hardcoded $DATADIR/bootstrap.dat filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; - if (filesystem::exists(pathBootstrap) && !fRequestShutdown) { + if (filesystem::exists(pathBootstrap)) { FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); if (file) { CImportingNow imp; @@ -401,9 +383,7 @@ void ThreadImport(void *data) { } // -loadblock= - BOOST_FOREACH(boost::filesystem::path &path, import->vFiles) { - if (fRequestShutdown) - break; + BOOST_FOREACH(boost::filesystem::path &path, vImportFiles) { FILE *file = fopen(path.string().c_str(), "rb"); if (file) { CImportingNow imp; @@ -411,10 +391,6 @@ void ThreadImport(void *data) { LoadExternalBlockFile(file); } } - - delete import; - - vnThreadsRunning[THREAD_IMPORT]--; } /** Initialize bitcoin. @@ -615,7 +591,7 @@ bool AppInit2(boost::thread_group& threadGroup) if (nScriptCheckThreads) { printf("Using %u threads for script verification\n", nScriptCheckThreads); for (int i=0; i vImportFiles; if (mapArgs.count("-loadblock")) { BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) - pimport->vFiles.push_back(strFile); + vImportFiles.push_back(strFile); } - NewThread(ThreadImport, pimport); + threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); // ********************************************************* Step 10: load peers @@ -1038,11 +1011,11 @@ 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, NULL)) + if (!NewThread(StartNode, (void*)&threadGroup)) InitError(_("Error: could not start node")); if (fServer) - NewThread(ThreadRPCServer, NULL); + StartRPCThreads(); // Generate coins in the background GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); -- cgit v1.2.3 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 a2e85f2ef..3ccd01e3b 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 From 723035bb6839c5d65bfee96d501a8c54814778e3 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Sat, 23 Mar 2013 18:14:12 -0400 Subject: Have Qt poll for shutdown requested, the QT way. --- src/init.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/init.cpp') diff --git a/src/init.cpp b/src/init.cpp index 3ccd01e3b..ee69d2207 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -61,8 +61,8 @@ enum BindFlags { // 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. +// fRequestShutdown getting set, and then does the normal Qt +// shutdown thing. // volatile bool fRequestShutdown = false; @@ -71,6 +71,10 @@ void StartShutdown() { fRequestShutdown = true; } +bool ShutdownRequested() +{ + return fRequestShutdown; +} static CCoinsViewDB *pcoinsdbview; -- cgit v1.2.3