diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 84478398..9c126fc3 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -30,13 +30,12 @@ using namespace boost; using namespace boost::asio; using namespace json_spirit; -void ThreadRPCServer2(void* parg); - static std::string strRPCUserColonPass; -const Object emptyobj; - -void ThreadRPCServer3(void* parg); +// These are created by StartRPCThreads, destroyed in StopRPCThreads +static asio::io_service* rpc_io_service = NULL; +static ssl::context* rpc_ssl_context = NULL; +static boost::thread_group* rpc_worker_group = NULL; static inline unsigned short GetDefaultRPCPort() { @@ -650,26 +649,7 @@ private: iostreams::stream< SSLIOStreamDevice > _stream; }; -void ThreadRPCServer(void* parg) -{ - // Make this thread recognisable as the RPC listener - RenameThread("bitcoin-rpclist"); - - try - { - vnThreadsRunning[THREAD_RPCLISTENER]++; - ThreadRPCServer2(parg); - vnThreadsRunning[THREAD_RPCLISTENER]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_RPCLISTENER]--; - PrintException(&e, "ThreadRPCServer()"); - } catch (...) { - vnThreadsRunning[THREAD_RPCLISTENER]--; - PrintException(NULL, "ThreadRPCServer()"); - } - printf("ThreadRPCServer exited\n"); -} +void ServiceConnection(AcceptedConnection *conn); // Forward declaration required for RPCListen template @@ -711,11 +691,8 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptoris_open()) + if (error != asio::error::operation_aborted && acceptor->is_open()) RPCListen(acceptor, context, fUseSSL); AcceptedConnectionImpl* tcp_conn = dynamic_cast< AcceptedConnectionImpl* >(conn); @@ -729,28 +706,22 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptorpeer.address())) + else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address())) { // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. if (!fUseSSL) conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; delete conn; } - - // start HTTP client thread - else if (!NewThread(ThreadRPCServer3, conn)) { - printf("Failed to create RPC server client thread\n"); + else { + ServiceConnection(conn); + conn->close(); delete conn; } - - vnThreadsRunning[THREAD_RPCLISTENER]--; } -void ThreadRPCServer2(void* parg) +void StartRPCThreads() { - printf("ThreadRPCServer started\n"); - strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; if ((mapArgs["-rpcpassword"] == "") || (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) @@ -781,27 +752,28 @@ void ThreadRPCServer2(void* parg) return; } - const bool fUseSSL = GetBoolArg("-rpcssl"); + assert(rpc_io_service == NULL); + rpc_io_service = new asio::io_service(); + rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); - asio::io_service io_service; + const bool fUseSSL = GetBoolArg("-rpcssl"); - ssl::context context(io_service, ssl::context::sslv23); if (fUseSSL) { - context.set_options(ssl::context::no_sslv2); + rpc_ssl_context->set_options(ssl::context::no_sslv2); filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; - if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string()); + if (filesystem::exists(pathCertFile)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; - if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem); + if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str()); + SSL_CTX_set_cipher_list(rpc_ssl_context->impl(), strCiphers.c_str()); } // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets @@ -809,9 +781,7 @@ void ThreadRPCServer2(void* parg) asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); boost::system::error_code v6_only_error; - boost::shared_ptr acceptor(new ip::tcp::acceptor(io_service)); - - boost::signals2::signal StopRequests; + boost::shared_ptr acceptor(new ip::tcp::acceptor(*rpc_io_service)); bool fListening = false; std::string strerr; @@ -826,11 +796,7 @@ void ThreadRPCServer2(void* parg) acceptor->bind(endpoint); acceptor->listen(socket_base::max_connections); - RPCListen(acceptor, context, fUseSSL); - // Cancel outstanding listen-requests for this acceptor when shutting down - StopRequests.connect(signals2::slot( - static_cast(&ip::tcp::acceptor::close), acceptor.get()) - .track(acceptor)); + RPCListen(acceptor, *rpc_ssl_context, fUseSSL); fListening = true; } @@ -846,17 +812,13 @@ void ThreadRPCServer2(void* parg) bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); endpoint.address(bindAddress); - acceptor.reset(new ip::tcp::acceptor(io_service)); + acceptor.reset(new ip::tcp::acceptor(*rpc_io_service)); acceptor->open(endpoint.protocol()); acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor->bind(endpoint); acceptor->listen(socket_base::max_connections); - RPCListen(acceptor, context, fUseSSL); - // Cancel outstanding listen-requests for this acceptor when shutting down - StopRequests.connect(signals2::slot( - static_cast(&ip::tcp::acceptor::close), acceptor.get()) - .track(acceptor)); + RPCListen(acceptor, *rpc_ssl_context, fUseSSL); fListening = true; } @@ -872,11 +834,20 @@ void ThreadRPCServer2(void* parg) return; } - vnThreadsRunning[THREAD_RPCLISTENER]--; - while (!fShutdown) - io_service.run_one(); - vnThreadsRunning[THREAD_RPCLISTENER]++; - StopRequests(); + rpc_worker_group = new boost::thread_group(); + for (int i = 0; i < GetArg("-rpcthreads", 4); i++) + rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); +} + +void StopRPCThreads() +{ + if (rpc_io_service == NULL) return; + + rpc_io_service->stop(); + rpc_worker_group->join_all(); + delete rpc_worker_group; rpc_worker_group = NULL; + delete rpc_ssl_context; rpc_ssl_context = NULL; + delete rpc_io_service; rpc_io_service = NULL; } class JSONRequest @@ -953,32 +924,11 @@ static string JSONRPCExecBatch(const Array& vReq) return write_string(Value(ret), false) + "\n"; } -static CCriticalSection cs_THREAD_RPCHANDLER; - -void ThreadRPCServer3(void* parg) +void ServiceConnection(AcceptedConnection *conn) { - // Make this thread recognisable as the RPC handler - RenameThread("bitcoin-rpchand"); - - { - LOCK(cs_THREAD_RPCHANDLER); - vnThreadsRunning[THREAD_RPCHANDLER]++; - } - AcceptedConnection *conn = (AcceptedConnection *) parg; - bool fRun = true; - loop { - if (fShutdown || !fRun) - { - conn->close(); - delete conn; - { - LOCK(cs_THREAD_RPCHANDLER); - --vnThreadsRunning[THREAD_RPCHANDLER]; - } - return; - } - + while (fRun) + { int nProto = 0; map mapHeaders; string strRequest, strMethod, strURI; @@ -1003,7 +953,7 @@ void ThreadRPCServer3(void* parg) If this results in a DOS the user really shouldn't have their RPC port exposed.*/ if (mapArgs["-rpcpassword"].size() < 20) - Sleep(250); + MilliSleep(250); conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; break; @@ -1049,12 +999,6 @@ void ThreadRPCServer3(void* parg) break; } } - - delete conn; - { - LOCK(cs_THREAD_RPCHANDLER); - vnThreadsRunning[THREAD_RPCHANDLER]--; - } } json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const @@ -1277,13 +1221,14 @@ int CommandLineRPC(int argc, char *argv[]) strPrint = write_string(result, true); } } - catch (std::exception& e) - { + catch (boost::thread_interrupted) { + throw; + } + catch (std::exception& e) { strPrint = string("error: ") + e.what(); nRet = 87; } - catch (...) - { + catch (...) { PrintException(NULL, "CommandLineRPC()"); } @@ -1321,6 +1266,9 @@ int main(int argc, char *argv[]) return CommandLineRPC(argc, argv); } } + catch (boost::thread_interrupted) { + throw; + } catch (std::exception& e) { PrintException(&e, "main()"); } catch (...) { diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 6a3554ae..315fd923 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -67,7 +67,8 @@ enum RPCErrorCode json_spirit::Object JSONRPCError(int code, const std::string& message); -void ThreadRPCServer(void* parg); +void StartRPCThreads(); +void StopRPCThreads(); int CommandLineRPC(int argc, char *argv[]); /** Convert parameter values for RPC call from strings to command-specific JSON objects. */ diff --git a/src/checkqueue.h b/src/checkqueue.h index 12dde36f..eba424fb 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -33,9 +33,6 @@ private: // Master thread blocks on this when out of work boost::condition_variable condMaster; - // Quit method blocks on this until all workers are gone - boost::condition_variable condQuit; - // The queue of elements to be processed. // As the order of booleans doesn't matter, it is used as a LIFO (stack) std::vector queue; @@ -85,8 +82,6 @@ private: while (queue.empty()) { if ((fMaster || fQuit) && nTodo == 0) { nTotal--; - if (nTotal==0) - condQuit.notify_one(); bool fRet = fAllOk; // reset the status for new work later if (fMaster) @@ -151,20 +146,7 @@ public: condWorker.notify_all(); } - // Shut the queue down - void Quit() { - boost::unique_lock lock(mutex); - fQuit = true; - // No need to wake the master, as he will quit automatically when all jobs are - // done. - condWorker.notify_all(); - - while (nTotal > 0) - condQuit.wait(lock); - } - ~CCheckQueue() { - Quit(); } friend class CCheckQueueControl; diff --git a/src/db.cpp b/src/db.cpp index e51d5d2c..35d6cca8 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -62,8 +62,7 @@ bool CDBEnv::Open(const boost::filesystem::path& path) if (fDbEnvInit) return true; - if (fShutdown) - return false; + boost::this_thread::interruption_point(); strPath = path.string(); filesystem::path pathLogDir = path / "database"; @@ -108,8 +107,7 @@ void CDBEnv::MakeMock() if (fDbEnvInit) throw runtime_error("CDBEnv::MakeMock(): already initialized"); - if (fShutdown) - throw runtime_error("CDBEnv::MakeMock(): during shutdown"); + boost::this_thread::interruption_point(); printf("CDBEnv::MakeMock()\n"); @@ -327,7 +325,7 @@ bool CDBEnv::RemoveDb(const string& strFile) bool CDB::Rewrite(const string& strFile, const char* pszSkip) { - while (!fShutdown) + while (true) { { LOCK(bitdb.cs_db); @@ -413,7 +411,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) return fSuccess; } } - Sleep(100); + MilliSleep(100); } return false; } diff --git a/src/db.h b/src/db.h index 9c01060a..206da91e 100644 --- a/src/db.h +++ b/src/db.h @@ -24,7 +24,7 @@ class CWalletTx; extern unsigned int nWalletDBUpdated; -void ThreadFlushWalletDB(void* parg); +void ThreadFlushWalletDB(const std::string& strWalletFile); bool BackupWallet(const CWallet& wallet, const std::string& strDest); diff --git a/src/init.cpp b/src/init.cpp index aac46d48..ee69d220 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -40,85 +40,82 @@ enum BindFlags { // Shutdown // -void ExitTimeout(void* parg) -{ -#ifdef WIN32 - Sleep(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, 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; +} +bool ShutdownRequested() +{ + return fRequestShutdown; } 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"); - - bool fFirstThread = false; + nTransactionsUpdated++; + StopRPCThreads(); + bitdb.Flush(false); + StopNode(); { - TRY_LOCK(cs_Shutdown, lockShutdown); - if (lockShutdown) - { - fFirstThread = !fTaken; - fTaken = true; - } + LOCK(cs_main); + if (pblocktree) + pblocktree->Flush(); + if (pcoinsTip) + pcoinsTip->Flush(); + delete pcoinsTip; pcoinsTip = NULL; + delete pcoinsdbview; pcoinsdbview = NULL; + delete pblocktree; pblocktree = NULL; } - static bool fExit; - if (fFirstThread) - { - fShutdown = true; - fRequestShutdown = true; - nTransactionsUpdated++; - bitdb.Flush(false); - { - LOCK(cs_main); - ThreadScriptCheckQuit(); - } - 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); - Sleep(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 + bitdb.Flush(true); + boost::filesystem::remove(GetPidFile()); + UnregisterWallet(pwalletMain); + delete pwalletMain; +} + +// +// Signal handlers are very limited in what they are allowed to do, so: +// +void DetectShutdownThread(boost::thread_group* threadGroup) +{ + // Tell the main threads to shutdown. + while (!fRequestShutdown) { - while (!fExit) - Sleep(500); - Sleep(100); - ExitThread(0); + MilliSleep(200); + if (fRequestShutdown) + threadGroup->interrupt_all(); } } @@ -143,6 +140,9 @@ void HandleSIGHUP(int) #if !defined(QT_GUI) bool AppInit(int argc, char* argv[]) { + boost::thread_group threadGroup; + boost::thread* detectShutdownThread = NULL; + bool fRet = false; try { @@ -154,7 +154,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,16 +184,52 @@ 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 - fRet = AppInit2(); + 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) { PrintExceptionContinue(&e, "AppInit()"); } catch (...) { PrintExceptionContinue(NULL, "AppInit()"); } - if (!fRet) - Shutdown(NULL); + if (!fRet) { + if (detectShutdownThread) + detectShutdownThread->interrupt(); + threadGroup.interrupt_all(); + } + + if (detectShutdownThread) + { + detectShutdownThread->join(); + delete detectShutdownThread; + detectShutdownThread = NULL; + } + Shutdown(); + return fRet; } @@ -210,7 +246,7 @@ int main(int argc, char* argv[]) if (fRet && fDaemon) return 0; - return 1; + return (fRet ? 0 : 1); } #endif @@ -298,6 +334,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 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" + @@ -339,22 +376,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) @@ -363,18 +394,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; @@ -386,9 +415,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; @@ -396,16 +423,12 @@ void ThreadImport(void *data) { LoadExternalBlockFile(file); } } - - delete import; - - vnThreadsRunning[THREAD_IMPORT]--; } /** 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 @@ -506,12 +529,6 @@ bool AppInit2() else fDebugNet = GetBoolArg("-debugnet"); -#if !defined(WIN32) && !defined(QT_GUI) - fDaemon = GetBoolArg("-daemon"); -#else - fDaemon = false; -#endif - if (fDaemon) fServer = true; else @@ -559,28 +576,6 @@ bool AppInit2() 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"); @@ -598,7 +593,7 @@ bool AppInit2() 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 @@ -1021,11 +1013,10 @@ bool AppInit2() printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size()); printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size()); - if (!NewThread(StartNode, NULL)) - InitError(_("Error: could not start node")); + StartNode(threadGroup); if (fServer) - NewThread(ThreadRPCServer, NULL); + StartRPCThreads(); // Generate coins in the background GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); @@ -1040,12 +1031,8 @@ bool AppInit2() // 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) - Sleep(5000); -#endif + // Run a thread to flush wallet periodically + threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); - return true; + return !fRequestShutdown; } diff --git a/src/init.h b/src/init.h index 8308ee64..5927670c 100644 --- a/src/init.h +++ b/src/init.h @@ -10,8 +10,9 @@ extern CWallet* pwalletMain; void StartShutdown(); -void Shutdown(void* parg); -bool AppInit2(); +bool ShutdownRequested(); +void Shutdown(); +bool AppInit2(boost::thread_group& threadGroup); std::string HelpMessage(); #endif diff --git a/src/main.cpp b/src/main.cpp index 52bf3faa..4f5febc4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,8 +61,8 @@ CScript COINBASE_FLAGS; const string strMessageMagic = "Bitcoin Signed Message:\n"; -double dHashesPerSec; -int64 nHPSTimerStart; +double dHashesPerSec = 0.0; +int64 nHPSTimerStart = 0; // Settings int64 nTransactionFee = 0; @@ -1256,8 +1256,7 @@ bool ConnectBestBlock(CValidationState &state) { if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { reverse(vAttach.begin(), vAttach.end()); BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { - if (fRequestShutdown) - break; + boost::this_thread::interruption_point(); try { if (!SetBestChain(state, pindexSwitch)) return false; @@ -1560,15 +1559,9 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne static CCheckQueue scriptcheckqueue(128); -void ThreadScriptCheck(void*) { - vnThreadsRunning[THREAD_SCRIPTCHECK]++; +void ThreadScriptCheck() { RenameThread("bitcoin-scriptch"); scriptcheckqueue.Thread(); - vnThreadsRunning[THREAD_SCRIPTCHECK]--; -} - -void ThreadScriptCheckQuit() { - scriptcheckqueue.Quit(); } bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) @@ -2464,7 +2457,6 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector &vMatch) { bool AbortNode(const std::string &strMessage) { - fRequestShutdown = true; strMiscWarning = strMessage; printf("*** %s\n", strMessage.c_str()); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); @@ -2543,8 +2535,7 @@ bool static LoadBlockIndexDB() if (!pblocktree->LoadBlockIndexGuts()) return false; - if (fRequestShutdown) - return true; + boost::this_thread::interruption_point(); // Calculate bnChainWork vector > vSortedByHeight; @@ -2624,7 +2615,8 @@ bool VerifyDB() { CValidationState state; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { - if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) + boost::this_thread::interruption_point(); + if (pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; // check level 0: read from disk @@ -2661,7 +2653,8 @@ bool VerifyDB() { // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { CBlockIndex *pindex = pindexState; - while (pindex != pindexBest && !fRequestShutdown) { + while (pindex != pindexBest) { + boost::this_thread::interruption_point(); pindex = pindex->pnext; CBlock block; if (!block.ReadFromDisk(pindex)) @@ -2868,7 +2861,9 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } } uint64 nRewind = blkdat.GetPos(); - while (blkdat.good() && !blkdat.eof() && !fRequestShutdown) { + while (blkdat.good() && !blkdat.eof()) { + boost::this_thread::interruption_point(); + blkdat.SetPos(nRewind); nRewind++; // start one byte further next time, in case of failure blkdat.SetLimit(); // remove former limit @@ -3043,8 +3038,7 @@ void static ProcessGetData(CNode* pfrom) const CInv &inv = *it; { - if (fShutdown) - break; + boost::this_thread::interruption_point(); it++; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) @@ -3302,8 +3296,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) int64 nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) { - if (fShutdown) - return true; + boost::this_thread::interruption_point(); + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); @@ -3371,8 +3365,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { const CInv &inv = vInv[nInv]; - if (fShutdown) - return true; + boost::this_thread::interruption_point(); pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); @@ -3804,8 +3797,7 @@ bool ProcessMessages(CNode* pfrom) LOCK(cs_main); fRet = ProcessMessage(pfrom, strCommand, vRecv); } - if (fShutdown) - break; + boost::this_thread::interruption_point(); } catch (std::ios_base::failure& e) { @@ -3824,6 +3816,9 @@ bool ProcessMessages(CNode* pfrom) PrintExceptionContinue(&e, "ProcessMessages()"); } } + catch (boost::thread_interrupted) { + throw; + } catch (std::exception& e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { @@ -4090,6 +4085,8 @@ unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1 nHashesDone = 0xffff+1; return (unsigned int) -1; } + if ((nNonce & 0xfff) == 0) + boost::this_thread::interruption_point(); } } @@ -4507,37 +4504,19 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) return true; } -void static ThreadBitcoinMiner(void* parg); - -static bool fGenerateBitcoins = false; -static bool fLimitProcessors = false; -static int nLimitProcessors = -1; - void static BitcoinMiner(CWallet *pwallet) { printf("BitcoinMiner started\n"); SetThreadPriority(THREAD_PRIORITY_LOWEST); - - // Make this thread recognisable as the mining thread RenameThread("bitcoin-miner"); // Each thread has its own key and counter CReserveKey reservekey(pwallet); unsigned int nExtraNonce = 0; - while (fGenerateBitcoins) - { - if (fShutdown) - return; - while (vNodes.empty() || IsInitialBlockDownload()) - { - Sleep(1000); - if (fShutdown) - return; - if (!fGenerateBitcoins) - return; - } - + try { loop { + while (vNodes.empty()) + MilliSleep(1000); // // Create new block @@ -4554,7 +4533,6 @@ void static BitcoinMiner(CWallet *pwallet) printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); - // // Pre-build hash buffers // @@ -4627,19 +4605,14 @@ void static BitcoinMiner(CWallet *pwallet) if (GetTime() - nLogTime > 30 * 60) { nLogTime = GetTime(); - printf("hashmeter %3d CPUs %6.0f khash/s\n", vnThreadsRunning[THREAD_MINER], dHashesPerSec/1000.0); + printf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0); } } } } // Check for stop or if block needs to be rebuilt - if (fShutdown) - return; - if (!fGenerateBitcoins) - return; - if (fLimitProcessors && vnThreadsRunning[THREAD_MINER] > nLimitProcessors) - return; + boost::this_thread::interruption_point(); if (vNodes.empty()) break; if (nBlockNonce >= 0xffff0000) @@ -4659,57 +4632,35 @@ void static BitcoinMiner(CWallet *pwallet) hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); } } - } -} - -void static ThreadBitcoinMiner(void* parg) -{ - CWallet* pwallet = (CWallet*)parg; - try + } } + catch (boost::thread_interrupted) { - vnThreadsRunning[THREAD_MINER]++; - BitcoinMiner(pwallet); - vnThreadsRunning[THREAD_MINER]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_MINER]--; - PrintException(&e, "ThreadBitcoinMiner()"); - } catch (...) { - vnThreadsRunning[THREAD_MINER]--; - PrintException(NULL, "ThreadBitcoinMiner()"); - } - nHPSTimerStart = 0; - if (vnThreadsRunning[THREAD_MINER] == 0) - dHashesPerSec = 0; - printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINER]); + printf("BitcoinMiner terminated\n"); + throw; + } } - void GenerateBitcoins(bool fGenerate, CWallet* pwallet) { - fGenerateBitcoins = fGenerate; - nLimitProcessors = GetArg("-genproclimit", -1); - if (nLimitProcessors == 0) - fGenerateBitcoins = false; - fLimitProcessors = (nLimitProcessors != -1); + static boost::thread_group* minerThreads = NULL; - if (fGenerate) + int nThreads = GetArg("-genproclimit", -1); + if (nThreads < 0) + nThreads = boost::thread::hardware_concurrency(); + + if (minerThreads != NULL) { - int nProcessors = boost::thread::hardware_concurrency(); - printf("%d processors\n", nProcessors); - if (nProcessors < 1) - nProcessors = 1; - if (fLimitProcessors && nProcessors > nLimitProcessors) - nProcessors = nLimitProcessors; - int nAddThreads = nProcessors - vnThreadsRunning[THREAD_MINER]; - printf("Starting %d BitcoinMiner threads\n", nAddThreads); - for (int i = 0; i < nAddThreads; i++) - { - if (!NewThread(ThreadBitcoinMiner, pwallet)) - printf("Error: NewThread(ThreadBitcoinMiner) failed\n"); - Sleep(10); - } + minerThreads->interrupt_all(); + delete minerThreads; + minerThreads = NULL; } + + if (nThreads == 0 || !fGenerate) + return; + + minerThreads = new boost::thread_group(); + for (int i = 0; i < nThreads; i++) + minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); } // Amount compression: diff --git a/src/main.h b/src/main.h index 43c3051e..c6bd77d3 100644 --- a/src/main.h +++ b/src/main.h @@ -150,12 +150,8 @@ CBlockIndex* FindBlockByHeight(int nHeight); bool ProcessMessages(CNode* pfrom); /** Send queued protocol messages to be sent to a give node */ bool SendMessages(CNode* pto, bool fSendTrickle); -/** Run the importer thread, which deals with reindexing, loading bootstrap.dat, and whatever is passed to -loadblock */ -void ThreadImport(void *parg); /** Run an instance of the script checking thread */ -void ThreadScriptCheck(void* parg); -/** Stop the script checking threads */ -void ThreadScriptCheckQuit(); +void ThreadScriptCheck(); /** Run the miner threads */ void GenerateBitcoins(bool fGenerate, CWallet* pwallet); /** Generate a new block, without valid proof-of-work */ diff --git a/src/net.cpp b/src/net.cpp index 669f44b6..f1ece2c2 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -26,14 +26,6 @@ using namespace boost; static const int MAX_OUTBOUND_CONNECTIONS = 8; -void ThreadMessageHandler2(void* parg); -void ThreadSocketHandler2(void* parg); -void ThreadOpenConnections2(void* parg); -void ThreadOpenAddedConnections2(void* parg); -#ifdef USE_UPNP -void ThreadMapPort2(void* parg); -#endif -void ThreadDNSAddressSeed2(void* parg); bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); @@ -46,7 +38,6 @@ struct LocalServiceInfo { // Global state variables // bool fDiscover = true; -bool fUseUPnP = false; uint64 nLocalServices = NODE_NETWORK; static CCriticalSection cs_mapLocalHost; static map mapLocalHost; @@ -54,7 +45,6 @@ static bool vfReachable[NET_MAX] = {}; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; uint64 nLocalHostNonce = 0; -array vnThreadsRunning; static std::vector vhListenSocket; CAddrMan addrman; @@ -156,8 +146,7 @@ bool RecvLine(SOCKET hSocket, string& strLine) } else if (nBytes <= 0) { - if (fShutdown) - return false; + boost::this_thread::interruption_point(); if (nBytes < 0) { int nErr = WSAGetLastError(); @@ -165,7 +154,7 @@ bool RecvLine(SOCKET hSocket, string& strLine) continue; if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) { - Sleep(10); + MilliSleep(10); continue; } } @@ -754,32 +743,10 @@ void SocketSendData(CNode *pnode) pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); } -void ThreadSocketHandler(void* parg) -{ - // Make this thread recognisable as the networking thread - RenameThread("bitcoin-net"); - - try - { - vnThreadsRunning[THREAD_SOCKETHANDLER]++; - ThreadSocketHandler2(parg); - vnThreadsRunning[THREAD_SOCKETHANDLER]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_SOCKETHANDLER]--; - PrintException(&e, "ThreadSocketHandler()"); - } catch (...) { - vnThreadsRunning[THREAD_SOCKETHANDLER]--; - throw; // support pthread_cancel() - } - printf("ThreadSocketHandler exited\n"); -} - static list vNodesDisconnected; -void ThreadSocketHandler2(void* parg) +void ThreadSocketHandler() { - printf("ThreadSocketHandler started\n"); unsigned int nPrevNodeCount = 0; loop { @@ -892,12 +859,10 @@ void ThreadSocketHandler2(void* parg) } } - vnThreadsRunning[THREAD_SOCKETHANDLER]--; int nSelect = select(have_fds ? hSocketMax + 1 : 0, &fdsetRecv, &fdsetSend, &fdsetError, &timeout); - vnThreadsRunning[THREAD_SOCKETHANDLER]++; - if (fShutdown) - return; + boost::this_thread::interruption_point(); + if (nSelect == SOCKET_ERROR) { if (have_fds) @@ -909,7 +874,7 @@ void ThreadSocketHandler2(void* parg) } FD_ZERO(&fdsetSend); FD_ZERO(&fdsetError); - Sleep(timeout.tv_usec/1000); + MilliSleep(timeout.tv_usec/1000); } @@ -984,8 +949,7 @@ void ThreadSocketHandler2(void* parg) } BOOST_FOREACH(CNode* pnode, vNodesCopy) { - if (fShutdown) - return; + boost::this_thread::interruption_point(); // // Receive @@ -1076,7 +1040,7 @@ void ThreadSocketHandler2(void* parg) pnode->Release(); } - Sleep(10); + MilliSleep(10); } } @@ -1089,31 +1053,8 @@ void ThreadSocketHandler2(void* parg) #ifdef USE_UPNP -void ThreadMapPort(void* parg) +void ThreadMapPort() { - // Make this thread recognisable as the UPnP thread - RenameThread("bitcoin-UPnP"); - - try - { - vnThreadsRunning[THREAD_UPNP]++; - ThreadMapPort2(parg); - vnThreadsRunning[THREAD_UPNP]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_UPNP]--; - PrintException(&e, "ThreadMapPort()"); - } catch (...) { - vnThreadsRunning[THREAD_UPNP]--; - PrintException(NULL, "ThreadMapPort()"); - } - printf("ThreadMapPort exited\n"); -} - -void ThreadMapPort2(void* parg) -{ - printf("ThreadMapPort started\n"); - std::string port = strprintf("%u", GetListenPort()); const char * multicastif = 0; const char * minissdpdpath = 0; @@ -1154,33 +1095,9 @@ void ThreadMapPort2(void* parg) } string strDesc = "Bitcoin " + FormatFullVersion(); -#ifndef UPNPDISCOVER_SUCCESS - /* miniupnpc 1.5 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0); -#else - /* miniupnpc 1.6 */ - r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, - port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0"); -#endif - if(r!=UPNPCOMMAND_SUCCESS) - printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); - else - printf("UPnP Port Mapping successful.\n"); - int i = 1; - loop { - if (fShutdown || !fUseUPnP) - { - r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); - printf("UPNP_DeletePortMapping() returned : %d\n", r); - freeUPNPDevlist(devlist); devlist = 0; - FreeUPNPUrls(&urls); - return; - } - if (i % 600 == 0) // Refresh every 20 minutes - { + try { + loop { #ifndef UPNPDISCOVER_SUCCESS /* miniupnpc 1.5 */ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, @@ -1196,33 +1113,49 @@ void ThreadMapPort2(void* parg) port.c_str(), port.c_str(), lanaddr, r, strupnperror(r)); else printf("UPnP Port Mapping successful.\n");; + + MilliSleep(20*60*1000); // Refresh every 20 minutes } - Sleep(2000); - i++; + } + catch (boost::thread_interrupted) + { + r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0); + printf("UPNP_DeletePortMapping() returned : %d\n", r); + freeUPNPDevlist(devlist); devlist = 0; + FreeUPNPUrls(&urls); + throw; } } else { printf("No valid UPnP IGDs found\n"); freeUPNPDevlist(devlist); devlist = 0; if (r != 0) FreeUPNPUrls(&urls); - loop { - if (fShutdown || !fUseUPnP) - return; - Sleep(2000); - } } } -void MapPort() +void MapPort(bool fUseUPnP) { - if (fUseUPnP && vnThreadsRunning[THREAD_UPNP] < 1) + static boost::thread* upnp_thread = NULL; + + if (fUseUPnP) { - if (!NewThread(ThreadMapPort, NULL)) - printf("Error: ThreadMapPort(ThreadMapPort) failed\n"); + if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + } + upnp_thread = new boost::thread(boost::bind(&TraceThread >, "upnp", &ThreadMapPort)); + } + else if (upnp_thread) { + upnp_thread->interrupt(); + upnp_thread->join(); + delete upnp_thread; + upnp_thread = NULL; } } + #else -void MapPort() +void MapPort(bool) { // Intentionally left blank. } @@ -1254,32 +1187,10 @@ static const char *strTestNetDNSSeed[][2] = { {NULL, NULL} }; -void ThreadDNSAddressSeed(void* parg) -{ - // Make this thread recognisable as the DNS seeding thread - RenameThread("bitcoin-dnsseed"); - - try - { - vnThreadsRunning[THREAD_DNSSEED]++; - ThreadDNSAddressSeed2(parg); - vnThreadsRunning[THREAD_DNSSEED]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_DNSSEED]--; - PrintException(&e, "ThreadDNSAddressSeed()"); - } catch (...) { - vnThreadsRunning[THREAD_DNSSEED]--; - throw; // support pthread_cancel() - } - printf("ThreadDNSAddressSeed exited\n"); -} - -void ThreadDNSAddressSeed2(void* parg) +void ThreadDNSAddressSeed() { static const char *(*strDNSSeed)[2] = fTestNet ? strTestNetDNSSeed : strMainNetDNSSeed; - printf("ThreadDNSAddressSeed started\n"); int found = 0; printf("Loading addresses from DNS seeds (could take a while)\n"); @@ -1409,57 +1320,6 @@ void DumpAddresses() addrman.size(), GetTimeMillis() - nStart); } -void ThreadDumpAddress2(void* parg) -{ - printf("ThreadDumpAddress started\n"); - - vnThreadsRunning[THREAD_DUMPADDRESS]++; - while (!fShutdown) - { - DumpAddresses(); - vnThreadsRunning[THREAD_DUMPADDRESS]--; - Sleep(100000); - vnThreadsRunning[THREAD_DUMPADDRESS]++; - } - vnThreadsRunning[THREAD_DUMPADDRESS]--; -} - -void ThreadDumpAddress(void* parg) -{ - // Make this thread recognisable as the address dumping thread - RenameThread("bitcoin-adrdump"); - - try - { - ThreadDumpAddress2(parg); - } - catch (std::exception& e) { - PrintException(&e, "ThreadDumpAddress()"); - } - printf("ThreadDumpAddress exited\n"); -} - -void ThreadOpenConnections(void* parg) -{ - // Make this thread recognisable as the connection opening thread - RenameThread("bitcoin-opencon"); - - try - { - vnThreadsRunning[THREAD_OPENCONNECTIONS]++; - ThreadOpenConnections2(parg); - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; - PrintException(&e, "ThreadOpenConnections()"); - } catch (...) { - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; - PrintException(NULL, "ThreadOpenConnections()"); - } - printf("ThreadOpenConnections exited\n"); -} - void static ProcessOneShot() { string strDest; @@ -1478,10 +1338,8 @@ void static ProcessOneShot() } } -void ThreadOpenConnections2(void* parg) +void ThreadOpenConnections() { - printf("ThreadOpenConnections started\n"); - // Connect to specific addresses if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { @@ -1494,12 +1352,10 @@ void ThreadOpenConnections2(void* parg) OpenNetworkConnection(addr, NULL, strAddr.c_str()); for (int i = 0; i < 10 && i < nLoop; i++) { - Sleep(500); - if (fShutdown) - return; + MilliSleep(500); } } - Sleep(500); + MilliSleep(500); } } @@ -1509,18 +1365,10 @@ void ThreadOpenConnections2(void* parg) { ProcessOneShot(); - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; - Sleep(500); - vnThreadsRunning[THREAD_OPENCONNECTIONS]++; - if (fShutdown) - return; - + MilliSleep(500); - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; CSemaphoreGrant grant(*semOutbound); - vnThreadsRunning[THREAD_OPENCONNECTIONS]++; - if (fShutdown) - return; + boost::this_thread::interruption_point(); // Add seed nodes if IRC isn't working if (addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet) @@ -1600,38 +1448,15 @@ void ThreadOpenConnections2(void* parg) } } -void ThreadOpenAddedConnections(void* parg) -{ - // Make this thread recognisable as the connection opening thread - RenameThread("bitcoin-opencon"); - - try - { - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; - ThreadOpenAddedConnections2(parg); - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - PrintException(&e, "ThreadOpenAddedConnections()"); - } catch (...) { - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - PrintException(NULL, "ThreadOpenAddedConnections()"); - } - printf("ThreadOpenAddedConnections exited\n"); -} - -void ThreadOpenAddedConnections2(void* parg) +void ThreadOpenAddedConnections() { - printf("ThreadOpenAddedConnections started\n"); - { LOCK(cs_vAddedNodes); vAddedNodes = mapMultiArgs["-addnode"]; } if (HaveNameProxy()) { - while(!fShutdown) { + while(true) { list lAddresses(0); { LOCK(cs_vAddedNodes); @@ -1642,15 +1467,10 @@ void ThreadOpenAddedConnections2(void* parg) CAddress addr; CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(addr, &grant, strAddNode.c_str()); - Sleep(500); - if (fShutdown) - return; + MilliSleep(500); } - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - Sleep(120000); // Retry every 2 minutes - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; + MilliSleep(120000); // Retry every 2 minutes } - return; } for (unsigned int i = 0; true; i++) @@ -1694,17 +1514,9 @@ void ThreadOpenAddedConnections2(void* parg) { CSemaphoreGrant grant(*semOutbound); OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); - Sleep(500); - if (fShutdown) - return; + MilliSleep(500); } - if (fShutdown) - return; - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; - Sleep(120000); // Retry every 2 minutes - vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++; - if (fShutdown) - return; + MilliSleep(120000); // Retry every 2 minutes } } @@ -1714,8 +1526,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu // // Initiate outbound network connection // - if (fShutdown) - return false; + boost::this_thread::interruption_point(); if (!strDest) if (IsLocal(addrConnect) || FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || @@ -1724,11 +1535,9 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu if (strDest && FindNode(strDest)) return false; - vnThreadsRunning[THREAD_OPENCONNECTIONS]--; CNode* pnode = ConnectNode(addrConnect, strDest); - vnThreadsRunning[THREAD_OPENCONNECTIONS]++; - if (fShutdown) - return false; + boost::this_thread::interruption_point(); + if (!pnode) return false; if (grantOutbound) @@ -1746,33 +1555,10 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu - -void ThreadMessageHandler(void* parg) +void ThreadMessageHandler() { - // Make this thread recognisable as the message handling thread - RenameThread("bitcoin-msghand"); - - try - { - vnThreadsRunning[THREAD_MESSAGEHANDLER]++; - ThreadMessageHandler2(parg); - vnThreadsRunning[THREAD_MESSAGEHANDLER]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_MESSAGEHANDLER]--; - PrintException(&e, "ThreadMessageHandler()"); - } catch (...) { - vnThreadsRunning[THREAD_MESSAGEHANDLER]--; - PrintException(NULL, "ThreadMessageHandler()"); - } - printf("ThreadMessageHandler exited\n"); -} - -void ThreadMessageHandler2(void* parg) -{ - printf("ThreadMessageHandler started\n"); SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL); - while (!fShutdown) + while (true) { vector vNodesCopy; { @@ -1798,8 +1584,7 @@ void ThreadMessageHandler2(void* parg) if (!ProcessMessages(pnode)) pnode->CloseSocketDisconnect(); } - if (fShutdown) - return; + boost::this_thread::interruption_point(); // Send messages { @@ -1807,8 +1592,7 @@ void ThreadMessageHandler2(void* parg) if (lockSend) SendMessages(pnode, pnode == pnodeTrickle); } - if (fShutdown) - return; + boost::this_thread::interruption_point(); } { @@ -1817,16 +1601,7 @@ void ThreadMessageHandler2(void* parg) pnode->Release(); } - // Wait and allow messages to bunch up. - // Reduce vnThreadsRunning so StopNode has permission to exit while - // we're sleeping, but we must always check fShutdown after doing this. - vnThreadsRunning[THREAD_MESSAGEHANDLER]--; - Sleep(100); - if (fRequestShutdown) - StartShutdown(); - vnThreadsRunning[THREAD_MESSAGEHANDLER]++; - if (fShutdown) - return; + MilliSleep(100); } } @@ -1998,7 +1773,7 @@ void static Discover() NewThread(ThreadGetMyExternalIP, NULL); } -void StartNode(void* parg) +void StartNode(boost::thread_group& threadGroup) { // Make this thread recognisable as the startup thread RenameThread("bitcoin-start"); @@ -2021,69 +1796,39 @@ void StartNode(void* parg) if (!GetBoolArg("-dnsseed", true)) printf("DNS seeding disabled\n"); else - if (!NewThread(ThreadDNSAddressSeed, NULL)) - printf("Error: NewThread(ThreadDNSAddressSeed) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread >, "dnsseed", &ThreadDNSAddressSeed)); +#ifdef USE_UPNP // Map ports with UPnP - if (fUseUPnP) - MapPort(); + MapPort(GetBoolArg("-upnp", USE_UPNP)); +#endif // Send and receive from sockets, accept connections - if (!NewThread(ThreadSocketHandler, NULL)) - printf("Error: NewThread(ThreadSocketHandler) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread, "net", &ThreadSocketHandler)); // Initiate outbound connections from -addnode - if (!NewThread(ThreadOpenAddedConnections, NULL)) - printf("Error: NewThread(ThreadOpenAddedConnections) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread, "addcon", &ThreadOpenAddedConnections)); // Initiate outbound connections - if (!NewThread(ThreadOpenConnections, NULL)) - printf("Error: NewThread(ThreadOpenConnections) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread, "opencon", &ThreadOpenConnections)); // Process messages - if (!NewThread(ThreadMessageHandler, NULL)) - printf("Error: NewThread(ThreadMessageHandler) failed\n"); + threadGroup.create_thread(boost::bind(&TraceThread, "msghand", &ThreadMessageHandler)); // Dump network addresses - if (!NewThread(ThreadDumpAddress, NULL)) - printf("Error; NewThread(ThreadDumpAddress) failed\n"); + threadGroup.create_thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, 10000)); } bool StopNode() { printf("StopNode()\n"); - fShutdown = true; + GenerateBitcoins(false, NULL); + MapPort(false); nTransactionsUpdated++; - int64 nStart = GetTime(); if (semOutbound) for (int i=0; ipost(); - do - { - int nThreadsRunning = 0; - for (int n = 0; n < THREAD_MAX; n++) - nThreadsRunning += vnThreadsRunning[n]; - if (nThreadsRunning == 0) - break; - if (GetTime() - nStart > 20) - break; - Sleep(20); - } while(true); - if (vnThreadsRunning[THREAD_SOCKETHANDLER] > 0) printf("ThreadSocketHandler still running\n"); - if (vnThreadsRunning[THREAD_OPENCONNECTIONS] > 0) printf("ThreadOpenConnections still running\n"); - if (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0) printf("ThreadMessageHandler still running\n"); - if (vnThreadsRunning[THREAD_MINER] > 0) printf("ThreadBitcoinMiner still running\n"); - if (vnThreadsRunning[THREAD_RPCLISTENER] > 0) printf("ThreadRPCListener still running\n"); - if (vnThreadsRunning[THREAD_RPCHANDLER] > 0) printf("ThreadsRPCServer still running\n"); -#ifdef USE_UPNP - if (vnThreadsRunning[THREAD_UPNP] > 0) printf("ThreadMapPort still running\n"); -#endif - if (vnThreadsRunning[THREAD_DNSSEED] > 0) printf("ThreadDNSAddressSeed still running\n"); - if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n"); - if (vnThreadsRunning[THREAD_DUMPADDRESS] > 0) printf("ThreadDumpAddresses still running\n"); - while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0) - Sleep(20); - Sleep(50); + MilliSleep(50); DumpAddresses(); return true; diff --git a/src/net.h b/src/net.h index 368e4cd4..65e83f90 100644 --- a/src/net.h +++ b/src/net.h @@ -37,10 +37,10 @@ void AddressCurrentlyConnected(const CService& addr); CNode* FindNode(const CNetAddr& ip); CNode* FindNode(const CService& ip); CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL, int64 nTimeout=0); -void MapPort(); +void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); -void StartNode(void* parg); +void StartNode(boost::thread_group& threadGroup); bool StopNode(); void SocketSendData(CNode *pnode); @@ -69,30 +69,9 @@ void SetReachable(enum Network net, bool fFlag = true); CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); -/** Thread types */ -enum threadId -{ - THREAD_SOCKETHANDLER, - THREAD_OPENCONNECTIONS, - THREAD_MESSAGEHANDLER, - THREAD_MINER, - THREAD_RPCLISTENER, - THREAD_UPNP, - THREAD_DNSSEED, - THREAD_ADDEDCONNECTIONS, - THREAD_DUMPADDRESS, - THREAD_RPCHANDLER, - THREAD_IMPORT, - THREAD_SCRIPTCHECK, - - THREAD_MAX -}; - extern bool fDiscover; -extern bool fUseUPnP; extern uint64 nLocalServices; extern uint64 nLocalHostNonce; -extern boost::array vnThreadsRunning; extern CAddrMan addrman; extern std::vector vNodes; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 2c47f30e..f079f3b0 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -11,6 +11,7 @@ #include "guiutil.h" #include "guiconstants.h" #include "init.h" +#include "util.h" #include "ui_interface.h" #include "paymentserver.h" @@ -87,11 +88,6 @@ static void InitMessage(const std::string &message) printf("init message: %s\n", message.c_str()); } -static void QueueShutdown() -{ - QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection); -} - /* Translate string to current locale using Qt. */ @@ -185,7 +181,6 @@ int main(int argc, char *argv[]) uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); uiInterface.InitMessage.connect(InitMessage); - uiInterface.QueueShutdown.connect(QueueShutdown); uiInterface.Translate.connect(Translate); // Show help message immediately after parsing command-line options (for "-lang") and setting locale, @@ -215,9 +210,16 @@ int main(int argc, char *argv[]) if (GUIUtil::GetStartOnSystemStartup()) GUIUtil::SetStartOnSystemStartup(true); + boost::thread_group threadGroup; + BitcoinGUI window; guiref = &window; - if(AppInit2()) + + QTimer* pollShutdownTimer = new QTimer(guiref); + QObject::connect(pollShutdownTimer, SIGNAL(timeout()), guiref, SLOT(detectShutdown())); + pollShutdownTimer->start(200); + + if(AppInit2(threadGroup)) { { // Put this in a block, so that the Model objects are cleaned up before @@ -258,7 +260,9 @@ int main(int argc, char *argv[]) guiref = 0; } // Shutdown the core and its threads, but don't exit Bitcoin-Qt here - Shutdown(NULL); + threadGroup.interrupt_all(); + threadGroup.join_all(); + Shutdown(); } else { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index adc6cd47..9da5b850 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -24,6 +24,7 @@ #include "rpcconsole.h" #include "ui_interface.h" #include "wallet.h" +#include "init.h" #ifdef Q_OS_MAC #include "macdockiconhandler.h" @@ -839,3 +840,9 @@ void BitcoinGUI::toggleHidden() { showNormalIfMinimized(true); } + +void BitcoinGUI::detectShutdown() +{ + if (ShutdownRequested()) + QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection); +} diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 8f44389f..aba81cb3 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -199,6 +199,9 @@ private slots: void showNormalIfMinimized(bool fToggleHidden = false); /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ void toggleHidden(); + + /** called by a timer to check if fRequestShutdown has been set **/ + void detectShutdown(); }; #endif // BITCOINGUI_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index cd3170a3..6b1b4e3d 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -219,9 +219,8 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("fMinimizeToTray", fMinimizeToTray); break; case MapPortUPnP: - fUseUPnP = value.toBool(); - settings.setValue("fUseUPnP", fUseUPnP); - MapPort(); + settings.setValue("fUseUPnP", value.toBool()); + MapPort(value.toBool()); break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 21eb2fd1..526a7b5b 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1289,7 +1289,7 @@ void ThreadCleanWalletPassphrase(void* parg) break; LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); - Sleep(nToSleep); + MilliSleep(nToSleep); ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); } while(1); diff --git a/src/test/bignum_tests.cpp b/src/test/bignum_tests.cpp index 5b898d14..196b7274 100644 --- a/src/test/bignum_tests.cpp +++ b/src/test/bignum_tests.cpp @@ -128,51 +128,51 @@ BOOST_AUTO_TEST_CASE(bignum_SetCompact) CBigNum num; num.SetCompact(0); BOOST_CHECK_EQUAL(num.GetHex(), "0"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0); + BOOST_CHECK_EQUAL(num.GetCompact(), 0U); num.SetCompact(0x00123456); BOOST_CHECK_EQUAL(num.GetHex(), "0"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0); + BOOST_CHECK_EQUAL(num.GetCompact(), 0U); num.SetCompact(0x01123456); BOOST_CHECK_EQUAL(num.GetHex(), "12"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0x01120000); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x01120000U); // Make sure that we don't generate compacts with the 0x00800000 bit set num = 0x80; - BOOST_CHECK_EQUAL(num.GetCompact(), 0x02008000); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x02008000U); num.SetCompact(0x01fedcba); BOOST_CHECK_EQUAL(num.GetHex(), "-7e"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0x01fe0000); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x01fe0000U); num.SetCompact(0x02123456); BOOST_CHECK_EQUAL(num.GetHex(), "1234"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0x02123400); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x02123400U); num.SetCompact(0x03123456); BOOST_CHECK_EQUAL(num.GetHex(), "123456"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0x03123456); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x03123456U); num.SetCompact(0x04123456); BOOST_CHECK_EQUAL(num.GetHex(), "12345600"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0x04123456); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x04123456U); num.SetCompact(0x04923456); BOOST_CHECK_EQUAL(num.GetHex(), "-12345600"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0x04923456); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x04923456U); num.SetCompact(0x05009234); BOOST_CHECK_EQUAL(num.GetHex(), "92340000"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0x05009234); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x05009234U); num.SetCompact(0x20123456); BOOST_CHECK_EQUAL(num.GetHex(), "1234560000000000000000000000000000000000000000000000000000000000"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0x20123456); + BOOST_CHECK_EQUAL(num.GetCompact(), 0x20123456U); num.SetCompact(0xff123456); BOOST_CHECK_EQUAL(num.GetHex(), "123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - BOOST_CHECK_EQUAL(num.GetCompact(), 0xff123456); + BOOST_CHECK_EQUAL(num.GetCompact(), 0xff123456U); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 7297bb9a..d6f836d3 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1) CScript s; s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(solutions.size(), 4); + BOOST_CHECK_EQUAL(solutions.size(), 4U); CTxDestination addr; BOOST_CHECK(!ExtractDestination(s, addr)); BOOST_CHECK(IsMine(keystore, s)); @@ -229,7 +229,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1) CScript s; s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(solutions.size(), 4); + BOOST_CHECK_EQUAL(solutions.size(), 4U); vector addrs; int nRequired; BOOST_CHECK(ExtractDestinations(s, whichType, addrs, nRequired)); diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index b5107193..3444726c 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); BOOST_CHECK(txTo.AreInputsStandard(coins)); - BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(coins), 1); + BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(coins), 1U); // Make sure adding crap to the scriptSigs makes them non-standard: for (int i = 0; i < 3; i++) @@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven); BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); - BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11); + BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11U); txToNonStd.vin[0].scriptSig.clear(); BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 90ac89f8..19ffdcab 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -29,13 +29,13 @@ BOOST_AUTO_TEST_CASE(varints) // decode for (int i = 0; i < 100000; i++) { - int j; + int j = -1; ss >> VARINT(j); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } for (uint64 i = 0; i < 100000000000ULL; i += 999999937) { - uint64 j; + uint64 j = -1; ss >> VARINT(j); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); } diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 59673f9b..1762680a 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -21,21 +21,21 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount) { // Test CScript::GetSigOpCount() CScript s1; - BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0); - BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0); + BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0U); + BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0U); uint160 dummy; s1 << OP_1 << dummy << dummy << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 2); + BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 2U); s1 << OP_IF << OP_CHECKSIG << OP_ENDIF; - BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3); - BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21); + BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U); + BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U); CScript p2sh; p2sh.SetDestination(s1.GetID()); CScript scriptSig; scriptSig << OP_0 << Serialize(s1); - BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U); std::vector keys; for (int i = 0; i < 3; i++) @@ -46,15 +46,15 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount) } CScript s2; s2.SetMultisig(1, keys); - BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3); - BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20); + BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U); + BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U); p2sh.SetDestination(s2.GetID()); - BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0); - BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U); CScript scriptSig2; scriptSig2 << OP_1 << dummy << dummy << Serialize(s2); - BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig2), 3); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig2), 3U); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 1116507a..1bf9a28a 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -17,6 +17,7 @@ extern void noui_connect(); struct TestingSetup { CCoinsViewDB *pcoinsdbview; boost::filesystem::path pathTemp; + boost::thread_group threadGroup; TestingSetup() { fPrintToDebugger = true; // don't want to write to debug.log file @@ -35,11 +36,12 @@ struct TestingSetup { RegisterWallet(pwalletMain); nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) - NewThread(ThreadScriptCheck, NULL); + threadGroup.create_thread(&ThreadScriptCheck); } ~TestingSetup() { - ThreadScriptCheckQuit(); + threadGroup.interrupt_all(); + threadGroup.join_all(); delete pwalletMain; pwalletMain = NULL; delete pcoinsTip; diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1b0ccad5..2d05794c 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -323,4 +323,62 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) } } +static int nCounter = 0; + +static void Count() +{ + ++nCounter; + MilliSleep(10); +} + +static void CountWithArg(int arg) +{ + nCounter += arg; + MilliSleep(10); +} + +BOOST_AUTO_TEST_CASE(util_loop_forever1) +{ + boost::thread_group threadGroup; + + threadGroup.create_thread(boost::bind(&LoopForever, "count", &Count, 1)); + MilliSleep(1); + threadGroup.interrupt_all(); + BOOST_CHECK_EQUAL(nCounter, 1); + nCounter = 0; +} + +BOOST_AUTO_TEST_CASE(util_loop_forever2) +{ + boost::thread_group threadGroup; + + boost::function f = boost::bind(&CountWithArg, 11); + threadGroup.create_thread(boost::bind(&LoopForever >, "count11", f, 11)); + MilliSleep(1); + threadGroup.interrupt_all(); + BOOST_CHECK_EQUAL(nCounter, 11); + nCounter = 0; +} + +BOOST_AUTO_TEST_CASE(util_threadtrace1) +{ + boost::thread_group threadGroup; + + threadGroup.create_thread(boost::bind(&TraceThread, "count11", &Count)); + threadGroup.join_all(); + BOOST_CHECK_EQUAL(nCounter, 1); + nCounter = 0; +} + +BOOST_AUTO_TEST_CASE(util_threadtrace2) +{ + boost::thread_group threadGroup; + + boost::function f = boost::bind(&CountWithArg, 11); + threadGroup.create_thread(boost::bind(&TraceThread >, "count11", f)); + threadGroup.join_all(); + BOOST_CHECK_EQUAL(nCounter, 11); + nCounter = 0; +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index b0b3c479..a4cbfaee 100644 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -104,22 +104,22 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // try making 34 cents from 1,2,5,10,20 - we can't do it exactly BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_GT(nValueRet, 34 * CENT); // but should get more than 34 cents - BOOST_CHECK_EQUAL(setCoinsRet.size(), 3); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible) + BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible) // when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5 BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 7 * CENT); - BOOST_CHECK_EQUAL(setCoinsRet.size(), 2); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // when we try making 8 cents, the smaller coins (1,2,5) are exactly enough. BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK(nValueRet == 8 * CENT); - BOOST_CHECK_EQUAL(setCoinsRet.size(), 3); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10) BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 10 * CENT); - BOOST_CHECK_EQUAL(setCoinsRet.size(), 1); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin empty_wallet(); @@ -137,26 +137,26 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20 BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin - BOOST_CHECK_EQUAL(setCoinsRet.size(), 1); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20 BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins - BOOST_CHECK_EQUAL(setCoinsRet.size(), 3); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30 // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18 BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin - BOOST_CHECK_EQUAL(setCoinsRet.size(), 1); // because in the event of a tie, the biggest coin wins + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins // now try making 11 cents. we should get 5+6 BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 11 * CENT); - BOOST_CHECK_EQUAL(setCoinsRet.size(), 2); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // check that the smallest bigger coin is used add_coin( 1*COIN); @@ -165,11 +165,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin - BOOST_CHECK_EQUAL(setCoinsRet.size(), 1); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin - BOOST_CHECK_EQUAL(setCoinsRet.size(), 1); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // empty the wallet and start again, now with fractions of a cent, to test sub-cent change avoidance empty_wallet(); @@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount - BOOST_CHECK_EQUAL(setCoinsRet.size(), 10); // in ten coins + BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins // if there's not enough in the smaller coins to make at least 1 cent change (0.5+0.6+0.7 < 1.0+1.0), // we need to try finding an exact subset anyway @@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin(1111 * CENT); BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1111 * CENT); // we get the bigger coin - BOOST_CHECK_EQUAL(setCoinsRet.size(), 1); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0) empty_wallet(); @@ -230,7 +230,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) add_coin(1111 * CENT); BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount - BOOST_CHECK_EQUAL(setCoinsRet.size(), 2); // in two coins 0.4+0.6 + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6 // test avoiding sub-cent change empty_wallet(); @@ -241,12 +241,12 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // trying to make 1.0001 from these three coins BOOST_CHECK( wallet.SelectCoinsMinConf(1.0001 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1.0105 * COIN); // we should get all coins - BOOST_CHECK_EQUAL(setCoinsRet.size(), 3); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // but if we try to make 0.999, we should take the bigger of the two small coins to avoid sub-cent change BOOST_CHECK( wallet.SelectCoinsMinConf(0.999 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1.01 * COIN); // we should get 1 + 0.01 - BOOST_CHECK_EQUAL(setCoinsRet.size(), 2); + BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // test randomness { diff --git a/src/txdb.cpp b/src/txdb.cpp index ae7aceb7..5b0527c7 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -115,12 +115,13 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) { pcursor->SeekToFirst(); while (pcursor->Valid()) { + boost::this_thread::interruption_point(); try { leveldb::Slice slKey = pcursor->key(); CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); char chType; ssKey >> chType; - if (chType == 'c' && !fRequestShutdown) { + if (chType == 'c') { leveldb::Slice slValue = pcursor->value(); CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); CCoins coins; @@ -178,12 +179,13 @@ bool CBlockTreeDB::LoadBlockIndexGuts() // Load mapBlockIndex while (pcursor->Valid()) { + boost::this_thread::interruption_point(); try { leveldb::Slice slKey = pcursor->key(); CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); char chType; ssKey >> chType; - if (chType == 'b' && !fRequestShutdown) { + if (chType == 'b') { leveldb::Slice slValue = pcursor->value(); CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); CDiskBlockIndex diskindex; diff --git a/src/ui_interface.h b/src/ui_interface.h index f7dbe208..5b0555c1 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -79,9 +79,6 @@ public: /** Progress message during initialization. */ boost::signals2::signal InitMessage; - /** Initiate client shutdown. */ - boost::signals2::signal QueueShutdown; - /** Translate a message to the native language of the user. */ boost::signals2::signal Translate; diff --git a/src/util.cpp b/src/util.cpp index fc3e846a..9f5e08a1 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -73,8 +73,6 @@ bool fDebug = false; bool fDebugNet = false; bool fPrintToConsole = false; bool fPrintToDebugger = false; -volatile bool fRequestShutdown = false; -bool fShutdown = false; bool fDaemon = false; bool fServer = false; bool fCommandLine = false; @@ -1431,9 +1429,12 @@ void RenameThread(const char* name) // removed. pthread_set_name_np(pthread_self(), name); -// This is XCode 10.6-and-later; bring back if we drop 10.5 support: -// #elif defined(MAC_OSX) -// pthread_setname_np(name); +#elif defined(MAC_OSX) && defined(__MAC_OS_X_VERSION_MAX_ALLOWED) + +// pthread_setname_np is XCode 10.6-and-later +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + pthread_setname_np(name); +#endif #else // Prevent warnings for unused parameters... diff --git a/src/util.h b/src/util.h index d129d136..4d7c81be 100644 --- a/src/util.h +++ b/src/util.h @@ -15,6 +15,8 @@ typedef int pid_t; /* define for Windows compatibility */ #endif #include +#include +#include #include #include @@ -98,13 +100,16 @@ T* alignup(T* p) #endif #else #define MAX_PATH 1024 -inline void Sleep(int64 n) +#endif + +inline void MilliSleep(int64 n) { - /*Boost has a year 2038 problem— if the request sleep time is past epoch+2^31 seconds the sleep returns instantly. - So we clamp our sleeps here to 10 years and hope that boost is fixed by 2028.*/ - boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n>315576000000LL?315576000000LL:n)); -} +#if BOOST_VERSION >= 105000 + boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); +#else + boost::this_thread::sleep(boost::posix_time::milliseconds(n)); #endif +} /* This GNU C extension enables the compiler to check the format string against the parameters provided. * X is the number of the "format string" parameter, and Y is the number of the first variadic parameter. @@ -129,8 +134,6 @@ extern bool fDebug; extern bool fDebugNet; extern bool fPrintToConsole; extern bool fPrintToDebugger; -extern volatile bool fRequestShutdown; -extern bool fShutdown; extern bool fDaemon; extern bool fServer; extern bool fCommandLine; @@ -522,5 +525,60 @@ inline uint32_t ByteReverse(uint32_t value) return (value<<16) | (value>>16); } -#endif +// Standard wrapper for do-something-forever thread functions. +// "Forever" really means until the thread is interrupted. +// Use it like: +// new boost::thread(boost::bind(&LoopForever, "dumpaddr", &DumpAddresses, 10000)); +// or maybe: +// boost::function f = boost::bind(&FunctionWithArg, argument); +// threadGroup.create_thread(boost::bind(&LoopForever >, "nothing", f, milliseconds)); +template void LoopForever(const char* name, Callable func, int64 msecs) +{ + std::string s = strprintf("bitcoin-%s", name); + RenameThread(s.c_str()); + printf("%s thread start\n", name); + try + { + while (1) + { + func(); + MilliSleep(msecs); + } + } + catch (boost::thread_interrupted) + { + printf("%s thread stop\n", name); + throw; + } + catch (std::exception& e) { + PrintException(&e, name); + } + catch (...) { + PrintException(NULL, name); + } +} +// .. and a wrapper that just calls func once +template void TraceThread(const char* name, Callable func) +{ + std::string s = strprintf("bitcoin-%s", name); + RenameThread(s.c_str()); + try + { + printf("%s thread start\n", name); + func(); + printf("%s thread exit\n", name); + } + catch (boost::thread_interrupted) + { + printf("%s thread interrupt\n", name); + throw; + } + catch (std::exception& e) { + PrintException(&e, name); + } + catch (...) { + PrintException(NULL, name); + } +} +#endif diff --git a/src/wallet.cpp b/src/wallet.cpp index eecb7d2d..5761b008 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1383,7 +1383,6 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) return nLoadWalletRet; fFirstRunRet = !vchDefaultKey.IsValid(); - NewThread(ThreadFlushWalletDB, &strWalletFile); return DB_LOAD_OK; } diff --git a/src/wallet.h b/src/wallet.h index ecfdafe2..e750bfd8 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -327,8 +327,7 @@ public: ~CReserveKey() { - if (!fShutdown) - ReturnKey(); + ReturnKey(); } void ReturnKey(); diff --git a/src/walletdb.cpp b/src/walletdb.cpp index fe9bce21..abaf5a0f 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -451,8 +451,10 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) } pcursor->close(); } - catch (...) - { + catch (boost::thread_interrupted) { + throw; + } + catch (...) { result = DB_CORRUPT; } @@ -482,12 +484,11 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) return result; } -void ThreadFlushWalletDB(void* parg) +void ThreadFlushWalletDB(const string& strFile) { // Make this thread recognisable as the wallet flushing thread RenameThread("bitcoin-wallet"); - const string& strFile = ((const string*)parg)[0]; static bool fOneThread; if (fOneThread) return; @@ -498,9 +499,9 @@ void ThreadFlushWalletDB(void* parg) unsigned int nLastSeen = nWalletDBUpdated; unsigned int nLastFlushed = nWalletDBUpdated; int64 nLastWalletUpdate = GetTime(); - while (!fShutdown) + while (true) { - Sleep(500); + MilliSleep(500); if (nLastSeen != nWalletDBUpdated) { @@ -522,8 +523,9 @@ void ThreadFlushWalletDB(void* parg) mi++; } - if (nRefCount == 0 && !fShutdown) + if (nRefCount == 0) { + boost::this_thread::interruption_point(); map::iterator mi = bitdb.mapFileUseCount.find(strFile); if (mi != bitdb.mapFileUseCount.end()) { @@ -548,7 +550,7 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) { if (!wallet.fFileBacked) return false; - while (!fShutdown) + while (true) { { LOCK(bitdb.cs_db); @@ -579,7 +581,7 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) } } } - Sleep(100); + MilliSleep(100); } return false; }