Browse Source

Merge pull request #2357 from gavinandresen/shutdowncleanup

Thread / shutdown cleanup
miguelfreitas
Gavin Andresen 12 years ago
parent
commit
a0a437c86a
  1. 150
      src/bitcoinrpc.cpp
  2. 3
      src/bitcoinrpc.h
  3. 18
      src/checkqueue.h
  4. 10
      src/db.cpp
  5. 2
      src/db.h
  6. 267
      src/init.cpp
  7. 5
      src/init.h
  8. 145
      src/main.cpp
  9. 6
      src/main.h
  10. 399
      src/net.cpp
  11. 25
      src/net.h
  12. 20
      src/qt/bitcoin.cpp
  13. 7
      src/qt/bitcoingui.cpp
  14. 3
      src/qt/bitcoingui.h
  15. 5
      src/qt/optionsmodel.cpp
  16. 2
      src/rpcwallet.cpp
  17. 24
      src/test/bignum_tests.cpp
  18. 4
      src/test/multisig_tests.cpp
  19. 4
      src/test/script_P2SH_tests.cpp
  20. 4
      src/test/serialize_tests.cpp
  21. 22
      src/test/sigopcount_tests.cpp
  22. 6
      src/test/test_bitcoin.cpp
  23. 58
      src/test/util_tests.cpp
  24. 30
      src/test/wallet_tests.cpp
  25. 6
      src/txdb.cpp
  26. 3
      src/ui_interface.h
  27. 11
      src/util.cpp
  28. 74
      src/util.h
  29. 1
      src/wallet.cpp
  30. 3
      src/wallet.h
  31. 20
      src/walletdb.cpp

150
src/bitcoinrpc.cpp

@ -30,13 +30,12 @@ using namespace boost;
using namespace boost::asio; using namespace boost::asio;
using namespace json_spirit; using namespace json_spirit;
void ThreadRPCServer2(void* parg);
static std::string strRPCUserColonPass; static std::string strRPCUserColonPass;
const Object emptyobj; // These are created by StartRPCThreads, destroyed in StopRPCThreads
static asio::io_service* rpc_io_service = NULL;
void ThreadRPCServer3(void* parg); static ssl::context* rpc_ssl_context = NULL;
static boost::thread_group* rpc_worker_group = NULL;
static inline unsigned short GetDefaultRPCPort() static inline unsigned short GetDefaultRPCPort()
{ {
@ -650,26 +649,7 @@ private:
iostreams::stream< SSLIOStreamDevice<Protocol> > _stream; iostreams::stream< SSLIOStreamDevice<Protocol> > _stream;
}; };
void ThreadRPCServer(void* parg) void ServiceConnection(AcceptedConnection *conn);
{
// 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");
}
// Forward declaration required for RPCListen // Forward declaration required for RPCListen
template <typename Protocol, typename SocketAcceptorService> template <typename Protocol, typename SocketAcceptorService>
@ -711,11 +691,8 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol,
AcceptedConnection* conn, AcceptedConnection* conn,
const boost::system::error_code& error) const boost::system::error_code& error)
{ {
vnThreadsRunning[THREAD_RPCLISTENER]++;
// Immediately start accepting new connections, except when we're cancelled or our socket is closed. // Immediately start accepting new connections, except when we're cancelled or our socket is closed.
if (error != asio::error::operation_aborted if (error != asio::error::operation_aborted && acceptor->is_open())
&& acceptor->is_open())
RPCListen(acceptor, context, fUseSSL); RPCListen(acceptor, context, fUseSSL);
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn); AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
@ -729,28 +706,22 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol,
// Restrict callers by IP. It is important to // Restrict callers by IP. It is important to
// do this before starting client thread, to filter out // do this before starting client thread, to filter out
// certain DoS and misbehaving clients. // certain DoS and misbehaving clients.
else if (tcp_conn else if (tcp_conn && !ClientAllowed(tcp_conn->peer.address()))
&& !ClientAllowed(tcp_conn->peer.address()))
{ {
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
if (!fUseSSL) if (!fUseSSL)
conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush;
delete conn; delete conn;
} }
else {
// start HTTP client thread ServiceConnection(conn);
else if (!NewThread(ThreadRPCServer3, conn)) { conn->close();
printf("Failed to create RPC server client thread\n");
delete conn; delete conn;
} }
vnThreadsRunning[THREAD_RPCLISTENER]--;
} }
void ThreadRPCServer2(void* parg) void StartRPCThreads()
{ {
printf("ThreadRPCServer started\n");
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
if ((mapArgs["-rpcpassword"] == "") || if ((mapArgs["-rpcpassword"] == "") ||
(mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"]))
@ -781,27 +752,28 @@ void ThreadRPCServer2(void* parg)
return; 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) 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")); filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; 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()); else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str());
filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; 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()); 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"); 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 // 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(); asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any();
ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort()));
boost::system::error_code v6_only_error; boost::system::error_code v6_only_error;
boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(io_service)); boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service));
boost::signals2::signal<void ()> StopRequests;
bool fListening = false; bool fListening = false;
std::string strerr; std::string strerr;
@ -826,11 +796,7 @@ void ThreadRPCServer2(void* parg)
acceptor->bind(endpoint); acceptor->bind(endpoint);
acceptor->listen(socket_base::max_connections); acceptor->listen(socket_base::max_connections);
RPCListen(acceptor, context, fUseSSL); RPCListen(acceptor, *rpc_ssl_context, fUseSSL);
// Cancel outstanding listen-requests for this acceptor when shutting down
StopRequests.connect(signals2::slot<void ()>(
static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get())
.track(acceptor));
fListening = true; fListening = true;
} }
@ -846,17 +812,13 @@ void ThreadRPCServer2(void* parg)
bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any();
endpoint.address(bindAddress); 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->open(endpoint.protocol());
acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor->bind(endpoint); acceptor->bind(endpoint);
acceptor->listen(socket_base::max_connections); acceptor->listen(socket_base::max_connections);
RPCListen(acceptor, context, fUseSSL); RPCListen(acceptor, *rpc_ssl_context, fUseSSL);
// Cancel outstanding listen-requests for this acceptor when shutting down
StopRequests.connect(signals2::slot<void ()>(
static_cast<void (ip::tcp::acceptor::*)()>(&ip::tcp::acceptor::close), acceptor.get())
.track(acceptor));
fListening = true; fListening = true;
} }
@ -872,11 +834,20 @@ void ThreadRPCServer2(void* parg)
return; return;
} }
vnThreadsRunning[THREAD_RPCLISTENER]--; rpc_worker_group = new boost::thread_group();
while (!fShutdown) for (int i = 0; i < GetArg("-rpcthreads", 4); i++)
io_service.run_one(); rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service));
vnThreadsRunning[THREAD_RPCLISTENER]++; }
StopRequests();
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 class JSONRequest
@ -953,32 +924,11 @@ static string JSONRPCExecBatch(const Array& vReq)
return write_string(Value(ret), false) + "\n"; return write_string(Value(ret), false) + "\n";
} }
static CCriticalSection cs_THREAD_RPCHANDLER; void ServiceConnection(AcceptedConnection *conn)
void ThreadRPCServer3(void* parg)
{ {
// 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; bool fRun = true;
loop { while (fRun)
if (fShutdown || !fRun) {
{
conn->close();
delete conn;
{
LOCK(cs_THREAD_RPCHANDLER);
--vnThreadsRunning[THREAD_RPCHANDLER];
}
return;
}
int nProto = 0; int nProto = 0;
map<string, string> mapHeaders; map<string, string> mapHeaders;
string strRequest, strMethod, strURI; string strRequest, strMethod, strURI;
@ -1003,7 +953,7 @@ void ThreadRPCServer3(void* parg)
If this results in a DOS the user really If this results in a DOS the user really
shouldn't have their RPC port exposed.*/ shouldn't have their RPC port exposed.*/
if (mapArgs["-rpcpassword"].size() < 20) if (mapArgs["-rpcpassword"].size() < 20)
Sleep(250); MilliSleep(250);
conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
break; break;
@ -1049,12 +999,6 @@ void ThreadRPCServer3(void* parg)
break; break;
} }
} }
delete conn;
{
LOCK(cs_THREAD_RPCHANDLER);
vnThreadsRunning[THREAD_RPCHANDLER]--;
}
} }
json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
@ -1277,13 +1221,14 @@ int CommandLineRPC(int argc, char *argv[])
strPrint = write_string(result, true); strPrint = write_string(result, true);
} }
} }
catch (std::exception& e) catch (boost::thread_interrupted) {
{ throw;
}
catch (std::exception& e) {
strPrint = string("error: ") + e.what(); strPrint = string("error: ") + e.what();
nRet = 87; nRet = 87;
} }
catch (...) catch (...) {
{
PrintException(NULL, "CommandLineRPC()"); PrintException(NULL, "CommandLineRPC()");
} }
@ -1321,6 +1266,9 @@ int main(int argc, char *argv[])
return CommandLineRPC(argc, argv); return CommandLineRPC(argc, argv);
} }
} }
catch (boost::thread_interrupted) {
throw;
}
catch (std::exception& e) { catch (std::exception& e) {
PrintException(&e, "main()"); PrintException(&e, "main()");
} catch (...) { } catch (...) {

3
src/bitcoinrpc.h

@ -67,7 +67,8 @@ enum RPCErrorCode
json_spirit::Object JSONRPCError(int code, const std::string& message); json_spirit::Object JSONRPCError(int code, const std::string& message);
void ThreadRPCServer(void* parg); void StartRPCThreads();
void StopRPCThreads();
int CommandLineRPC(int argc, char *argv[]); int CommandLineRPC(int argc, char *argv[]);
/** Convert parameter values for RPC call from strings to command-specific JSON objects. */ /** Convert parameter values for RPC call from strings to command-specific JSON objects. */

18
src/checkqueue.h

@ -33,9 +33,6 @@ private:
// Master thread blocks on this when out of work // Master thread blocks on this when out of work
boost::condition_variable condMaster; 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. // The queue of elements to be processed.
// As the order of booleans doesn't matter, it is used as a LIFO (stack) // As the order of booleans doesn't matter, it is used as a LIFO (stack)
std::vector<T> queue; std::vector<T> queue;
@ -85,8 +82,6 @@ private:
while (queue.empty()) { while (queue.empty()) {
if ((fMaster || fQuit) && nTodo == 0) { if ((fMaster || fQuit) && nTodo == 0) {
nTotal--; nTotal--;
if (nTotal==0)
condQuit.notify_one();
bool fRet = fAllOk; bool fRet = fAllOk;
// reset the status for new work later // reset the status for new work later
if (fMaster) if (fMaster)
@ -151,20 +146,7 @@ public:
condWorker.notify_all(); condWorker.notify_all();
} }
// Shut the queue down
void Quit() {
boost::unique_lock<boost::mutex> 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() { ~CCheckQueue() {
Quit();
} }
friend class CCheckQueueControl<T>; friend class CCheckQueueControl<T>;

10
src/db.cpp

@ -62,8 +62,7 @@ bool CDBEnv::Open(const boost::filesystem::path& path)
if (fDbEnvInit) if (fDbEnvInit)
return true; return true;
if (fShutdown) boost::this_thread::interruption_point();
return false;
strPath = path.string(); strPath = path.string();
filesystem::path pathLogDir = path / "database"; filesystem::path pathLogDir = path / "database";
@ -108,8 +107,7 @@ void CDBEnv::MakeMock()
if (fDbEnvInit) if (fDbEnvInit)
throw runtime_error("CDBEnv::MakeMock(): already initialized"); throw runtime_error("CDBEnv::MakeMock(): already initialized");
if (fShutdown) boost::this_thread::interruption_point();
throw runtime_error("CDBEnv::MakeMock(): during shutdown");
printf("CDBEnv::MakeMock()\n"); printf("CDBEnv::MakeMock()\n");
@ -327,7 +325,7 @@ bool CDBEnv::RemoveDb(const string& strFile)
bool CDB::Rewrite(const string& strFile, const char* pszSkip) bool CDB::Rewrite(const string& strFile, const char* pszSkip)
{ {
while (!fShutdown) while (true)
{ {
{ {
LOCK(bitdb.cs_db); LOCK(bitdb.cs_db);
@ -413,7 +411,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip)
return fSuccess; return fSuccess;
} }
} }
Sleep(100); MilliSleep(100);
} }
return false; return false;
} }

2
src/db.h

@ -24,7 +24,7 @@ class CWalletTx;
extern unsigned int nWalletDBUpdated; extern unsigned int nWalletDBUpdated;
void ThreadFlushWalletDB(void* parg); void ThreadFlushWalletDB(const std::string& strWalletFile);
bool BackupWallet(const CWallet& wallet, const std::string& strDest); bool BackupWallet(const CWallet& wallet, const std::string& strDest);

267
src/init.cpp

@ -40,85 +40,82 @@ enum BindFlags {
// Shutdown // Shutdown
// //
void ExitTimeout(void* parg) //
{ // Thread management and startup/shutdown:
#ifdef WIN32 //
Sleep(5000); // The network-processing threads are all part of a thread group
ExitProcess(0); // created by AppInit() or the Qt main() function.
#endif //
} // 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() void StartShutdown()
{ {
#ifdef QT_GUI fRequestShutdown = true;
// ensure we leave the Qt main loop for a clean GUI exit (Shutdown() is called in bitcoin.cpp afterwards) }
uiInterface.QueueShutdown(); bool ShutdownRequested()
#else {
// Without UI, Shutdown() can simply be started in a new thread return fRequestShutdown;
NewThread(Shutdown, NULL);
#endif
} }
static CCoinsViewDB *pcoinsdbview; static CCoinsViewDB *pcoinsdbview;
void Shutdown(void* parg) void Shutdown()
{ {
static CCriticalSection cs_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"); RenameThread("bitcoin-shutoff");
nTransactionsUpdated++;
bool fFirstThread = false; StopRPCThreads();
bitdb.Flush(false);
StopNode();
{ {
TRY_LOCK(cs_Shutdown, lockShutdown); LOCK(cs_main);
if (lockShutdown) if (pblocktree)
{ pblocktree->Flush();
fFirstThread = !fTaken; if (pcoinsTip)
fTaken = true; pcoinsTip->Flush();
} delete pcoinsTip; pcoinsTip = NULL;
delete pcoinsdbview; pcoinsdbview = NULL;
delete pblocktree; pblocktree = NULL;
} }
static bool fExit; bitdb.Flush(true);
if (fFirstThread) boost::filesystem::remove(GetPidFile());
{ UnregisterWallet(pwalletMain);
fShutdown = true; delete pwalletMain;
fRequestShutdown = true; }
nTransactionsUpdated++;
bitdb.Flush(false); //
{ // Signal handlers are very limited in what they are allowed to do, so:
LOCK(cs_main); //
ThreadScriptCheckQuit(); void DetectShutdownThread(boost::thread_group* threadGroup)
} {
StopNode(); // Tell the main threads to shutdown.
{ while (!fRequestShutdown)
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
{ {
while (!fExit) MilliSleep(200);
Sleep(500); if (fRequestShutdown)
Sleep(100); threadGroup->interrupt_all();
ExitThread(0);
} }
} }
@ -143,6 +140,9 @@ void HandleSIGHUP(int)
#if !defined(QT_GUI) #if !defined(QT_GUI)
bool AppInit(int argc, char* argv[]) bool AppInit(int argc, char* argv[])
{ {
boost::thread_group threadGroup;
boost::thread* detectShutdownThread = NULL;
bool fRet = false; bool fRet = false;
try try
{ {
@ -154,7 +154,7 @@ bool AppInit(int argc, char* argv[])
if (!boost::filesystem::is_directory(GetDataDir(false))) if (!boost::filesystem::is_directory(GetDataDir(false)))
{ {
fprintf(stderr, "Error: Specified directory does not exist\n"); fprintf(stderr, "Error: Specified directory does not exist\n");
Shutdown(NULL); Shutdown();
} }
ReadConfigFile(mapArgs, mapMultiArgs); ReadConfigFile(mapArgs, mapMultiArgs);
@ -184,16 +184,52 @@ bool AppInit(int argc, char* argv[])
int ret = CommandLineRPC(argc, argv); int ret = CommandLineRPC(argc, argv);
exit(ret); 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) { catch (std::exception& e) {
PrintExceptionContinue(&e, "AppInit()"); PrintExceptionContinue(&e, "AppInit()");
} catch (...) { } catch (...) {
PrintExceptionContinue(NULL, "AppInit()"); PrintExceptionContinue(NULL, "AppInit()");
} }
if (!fRet) if (!fRet) {
Shutdown(NULL); if (detectShutdownThread)
detectShutdownThread->interrupt();
threadGroup.interrupt_all();
}
if (detectShutdownThread)
{
detectShutdownThread->join();
delete detectShutdownThread;
detectShutdownThread = NULL;
}
Shutdown();
return fRet; return fRet;
} }
@ -210,7 +246,7 @@ int main(int argc, char* argv[])
if (fRet && fDaemon) if (fRet && fDaemon)
return 0; return 0;
return 1; return (fRet ? 0 : 1);
} }
#endif #endif
@ -298,6 +334,7 @@ std::string HelpMessage()
" -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n" + " -rpcport=<port> " + _("Listen for JSON-RPC connections on <port> (default: 8332 or testnet: 18332)") + "\n" +
" -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n" + " -rpcallowip=<ip> " + _("Allow JSON-RPC connections from specified IP address") + "\n" +
" -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" + " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" +
" -rpcthreads=<n> " + _("Use this many threads to service RPC calls (default: 4)") + "\n" +
" -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" + " -blocknotify=<cmd> " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
" -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" + " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n" +
" -alertnotify=<cmd> " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n" + " -alertnotify=<cmd> " + _("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<boost::filesystem::path> vFiles;
};
void ThreadImport(void *data) {
CImportData *import = reinterpret_cast<CImportData*>(data);
void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
{
RenameThread("bitcoin-loadblk"); RenameThread("bitcoin-loadblk");
vnThreadsRunning[THREAD_IMPORT]++;
// -reindex // -reindex
if (fReindex) { if (fReindex) {
CImportingNow imp; CImportingNow imp;
int nFile = 0; int nFile = 0;
while (!fRequestShutdown) { while (true) {
CDiskBlockPos pos(nFile, 0); CDiskBlockPos pos(nFile, 0);
FILE *file = OpenBlockFile(pos, true); FILE *file = OpenBlockFile(pos, true);
if (!file) if (!file)
@ -363,18 +394,16 @@ void ThreadImport(void *data) {
LoadExternalBlockFile(file, &pos); LoadExternalBlockFile(file, &pos);
nFile++; nFile++;
} }
if (!fRequestShutdown) { pblocktree->WriteReindexing(false);
pblocktree->WriteReindexing(false); fReindex = false;
fReindex = false; printf("Reindexing finished\n");
printf("Reindexing finished\n"); // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
// To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): InitBlockIndex();
InitBlockIndex();
}
} }
// hardcoded $DATADIR/bootstrap.dat // hardcoded $DATADIR/bootstrap.dat
filesystem::path pathBootstrap = GetDataDir() / "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"); FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
if (file) { if (file) {
CImportingNow imp; CImportingNow imp;
@ -386,9 +415,7 @@ void ThreadImport(void *data) {
} }
// -loadblock= // -loadblock=
BOOST_FOREACH(boost::filesystem::path &path, import->vFiles) { BOOST_FOREACH(boost::filesystem::path &path, vImportFiles) {
if (fRequestShutdown)
break;
FILE *file = fopen(path.string().c_str(), "rb"); FILE *file = fopen(path.string().c_str(), "rb");
if (file) { if (file) {
CImportingNow imp; CImportingNow imp;
@ -396,16 +423,12 @@ void ThreadImport(void *data) {
LoadExternalBlockFile(file); LoadExternalBlockFile(file);
} }
} }
delete import;
vnThreadsRunning[THREAD_IMPORT]--;
} }
/** Initialize bitcoin. /** Initialize bitcoin.
* @pre Parameters should be parsed and config file should be read. * @pre Parameters should be parsed and config file should be read.
*/ */
bool AppInit2() bool AppInit2(boost::thread_group& threadGroup)
{ {
// ********************************************************* Step 1: setup // ********************************************************* Step 1: setup
#ifdef _MSC_VER #ifdef _MSC_VER
@ -506,12 +529,6 @@ bool AppInit2()
else else
fDebugNet = GetBoolArg("-debugnet"); fDebugNet = GetBoolArg("-debugnet");
#if !defined(WIN32) && !defined(QT_GUI)
fDaemon = GetBoolArg("-daemon");
#else
fDaemon = false;
#endif
if (fDaemon) if (fDaemon)
fServer = true; fServer = true;
else else
@ -559,28 +576,6 @@ bool AppInit2()
if (!lock.try_lock()) if (!lock.try_lock())
return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); 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)) if (GetBoolArg("-shrinkdebugfile", !fDebug))
ShrinkDebugFile(); ShrinkDebugFile();
printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); 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) { if (nScriptCheckThreads) {
printf("Using %u threads for script verification\n", nScriptCheckThreads); printf("Using %u threads for script verification\n", nScriptCheckThreads);
for (int i=0; i<nScriptCheckThreads-1; i++) for (int i=0; i<nScriptCheckThreads-1; i++)
NewThread(ThreadScriptCheck, NULL); threadGroup.create_thread(&ThreadScriptCheck);
} }
int64 nStart; int64 nStart;
@ -699,9 +694,6 @@ bool AppInit2()
fNoListen = !GetBoolArg("-listen", true); fNoListen = !GetBoolArg("-listen", true);
fDiscover = GetBoolArg("-discover", true); fDiscover = GetBoolArg("-discover", true);
fNameLookup = GetBoolArg("-dns", true); fNameLookup = GetBoolArg("-dns", true);
#ifdef USE_UPNP
fUseUPnP = GetBoolArg("-upnp", USE_UPNP);
#endif
bool fBound = false; bool fBound = false;
if (!fNoListen) { if (!fNoListen) {
@ -984,13 +976,13 @@ bool AppInit2()
if (!ConnectBestBlock(state)) if (!ConnectBestBlock(state))
strErrors << "Failed to connect best block"; strErrors << "Failed to connect best block";
CImportData *pimport = new CImportData(); std::vector<boost::filesystem::path> vImportFiles;
if (mapArgs.count("-loadblock")) if (mapArgs.count("-loadblock"))
{ {
BOOST_FOREACH(string strFile, mapMultiArgs["-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 // ********************************************************* Step 10: load peers
@ -1021,11 +1013,10 @@ bool AppInit2()
printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size()); printf("mapWallet.size() = %"PRIszu"\n", pwalletMain->mapWallet.size());
printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size()); printf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain->mapAddressBook.size());
if (!NewThread(StartNode, NULL)) StartNode(threadGroup);
InitError(_("Error: could not start node"));
if (fServer) if (fServer)
NewThread(ThreadRPCServer, NULL); StartRPCThreads();
// Generate coins in the background // Generate coins in the background
GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain); GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain);
@ -1040,12 +1031,8 @@ bool AppInit2()
// Add wallet transactions that aren't already in a block to mapTransactions // Add wallet transactions that aren't already in a block to mapTransactions
pwalletMain->ReacceptWalletTransactions(); pwalletMain->ReacceptWalletTransactions();
#if !defined(QT_GUI) // Run a thread to flush wallet periodically
// Loop until process is exit()ed from shutdown() function, threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile)));
// called from ThreadRPCServer thread when a "stop" command is received.
while (1)
Sleep(5000);
#endif
return true; return !fRequestShutdown;
} }

5
src/init.h

@ -10,8 +10,9 @@
extern CWallet* pwalletMain; extern CWallet* pwalletMain;
void StartShutdown(); void StartShutdown();
void Shutdown(void* parg); bool ShutdownRequested();
bool AppInit2(); void Shutdown();
bool AppInit2(boost::thread_group& threadGroup);
std::string HelpMessage(); std::string HelpMessage();
#endif #endif

145
src/main.cpp

@ -61,8 +61,8 @@ CScript COINBASE_FLAGS;
const string strMessageMagic = "Bitcoin Signed Message:\n"; const string strMessageMagic = "Bitcoin Signed Message:\n";
double dHashesPerSec; double dHashesPerSec = 0.0;
int64 nHPSTimerStart; int64 nHPSTimerStart = 0;
// Settings // Settings
int64 nTransactionFee = 0; int64 nTransactionFee = 0;
@ -1256,8 +1256,7 @@ bool ConnectBestBlock(CValidationState &state) {
if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) {
reverse(vAttach.begin(), vAttach.end()); reverse(vAttach.begin(), vAttach.end());
BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) {
if (fRequestShutdown) boost::this_thread::interruption_point();
break;
try { try {
if (!SetBestChain(state, pindexSwitch)) if (!SetBestChain(state, pindexSwitch))
return false; return false;
@ -1560,15 +1559,9 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
static CCheckQueue<CScriptCheck> scriptcheckqueue(128); static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
void ThreadScriptCheck(void*) { void ThreadScriptCheck() {
vnThreadsRunning[THREAD_SCRIPTCHECK]++;
RenameThread("bitcoin-scriptch"); RenameThread("bitcoin-scriptch");
scriptcheckqueue.Thread(); scriptcheckqueue.Thread();
vnThreadsRunning[THREAD_SCRIPTCHECK]--;
}
void ThreadScriptCheckQuit() {
scriptcheckqueue.Quit();
} }
bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
@ -2464,7 +2457,6 @@ uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
bool AbortNode(const std::string &strMessage) { bool AbortNode(const std::string &strMessage) {
fRequestShutdown = true;
strMiscWarning = strMessage; strMiscWarning = strMessage;
printf("*** %s\n", strMessage.c_str()); printf("*** %s\n", strMessage.c_str());
uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_ERROR);
@ -2543,8 +2535,7 @@ bool static LoadBlockIndexDB()
if (!pblocktree->LoadBlockIndexGuts()) if (!pblocktree->LoadBlockIndexGuts())
return false; return false;
if (fRequestShutdown) boost::this_thread::interruption_point();
return true;
// Calculate bnChainWork // Calculate bnChainWork
vector<pair<int, CBlockIndex*> > vSortedByHeight; vector<pair<int, CBlockIndex*> > vSortedByHeight;
@ -2624,7 +2615,8 @@ bool VerifyDB() {
CValidationState state; CValidationState state;
for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) 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; break;
CBlock block; CBlock block;
// check level 0: read from disk // check level 0: read from disk
@ -2661,7 +2653,8 @@ bool VerifyDB() {
// check level 4: try reconnecting blocks // check level 4: try reconnecting blocks
if (nCheckLevel >= 4) { if (nCheckLevel >= 4) {
CBlockIndex *pindex = pindexState; CBlockIndex *pindex = pindexState;
while (pindex != pindexBest && !fRequestShutdown) { while (pindex != pindexBest) {
boost::this_thread::interruption_point();
pindex = pindex->pnext; pindex = pindex->pnext;
CBlock block; CBlock block;
if (!block.ReadFromDisk(pindex)) if (!block.ReadFromDisk(pindex))
@ -2868,7 +2861,9 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
} }
} }
uint64 nRewind = blkdat.GetPos(); uint64 nRewind = blkdat.GetPos();
while (blkdat.good() && !blkdat.eof() && !fRequestShutdown) { while (blkdat.good() && !blkdat.eof()) {
boost::this_thread::interruption_point();
blkdat.SetPos(nRewind); blkdat.SetPos(nRewind);
nRewind++; // start one byte further next time, in case of failure nRewind++; // start one byte further next time, in case of failure
blkdat.SetLimit(); // remove former limit blkdat.SetLimit(); // remove former limit
@ -3043,8 +3038,7 @@ void static ProcessGetData(CNode* pfrom)
const CInv &inv = *it; const CInv &inv = *it;
{ {
if (fShutdown) boost::this_thread::interruption_point();
break;
it++; it++;
if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) 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; int64 nSince = nNow - 10 * 60;
BOOST_FOREACH(CAddress& addr, vAddr) BOOST_FOREACH(CAddress& addr, vAddr)
{ {
if (fShutdown) boost::this_thread::interruption_point();
return true;
if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
addr.nTime = nNow - 5 * 24 * 60 * 60; addr.nTime = nNow - 5 * 24 * 60 * 60;
pfrom->AddAddressKnown(addr); pfrom->AddAddressKnown(addr);
@ -3371,8 +3365,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{ {
const CInv &inv = vInv[nInv]; const CInv &inv = vInv[nInv];
if (fShutdown) boost::this_thread::interruption_point();
return true;
pfrom->AddInventoryKnown(inv); pfrom->AddInventoryKnown(inv);
bool fAlreadyHave = AlreadyHave(inv); bool fAlreadyHave = AlreadyHave(inv);
@ -3804,8 +3797,7 @@ bool ProcessMessages(CNode* pfrom)
LOCK(cs_main); LOCK(cs_main);
fRet = ProcessMessage(pfrom, strCommand, vRecv); fRet = ProcessMessage(pfrom, strCommand, vRecv);
} }
if (fShutdown) boost::this_thread::interruption_point();
break;
} }
catch (std::ios_base::failure& e) catch (std::ios_base::failure& e)
{ {
@ -3824,6 +3816,9 @@ bool ProcessMessages(CNode* pfrom)
PrintExceptionContinue(&e, "ProcessMessages()"); PrintExceptionContinue(&e, "ProcessMessages()");
} }
} }
catch (boost::thread_interrupted) {
throw;
}
catch (std::exception& e) { catch (std::exception& e) {
PrintExceptionContinue(&e, "ProcessMessages()"); PrintExceptionContinue(&e, "ProcessMessages()");
} catch (...) { } catch (...) {
@ -4090,6 +4085,8 @@ unsigned int static ScanHash_CryptoPP(char* pmidstate, char* pdata, char* phash1
nHashesDone = 0xffff+1; nHashesDone = 0xffff+1;
return (unsigned int) -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; return true;
} }
void static ThreadBitcoinMiner(void* parg);
static bool fGenerateBitcoins = false;
static bool fLimitProcessors = false;
static int nLimitProcessors = -1;
void static BitcoinMiner(CWallet *pwallet) void static BitcoinMiner(CWallet *pwallet)
{ {
printf("BitcoinMiner started\n"); printf("BitcoinMiner started\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST); SetThreadPriority(THREAD_PRIORITY_LOWEST);
// Make this thread recognisable as the mining thread
RenameThread("bitcoin-miner"); RenameThread("bitcoin-miner");
// Each thread has its own key and counter // Each thread has its own key and counter
CReserveKey reservekey(pwallet); CReserveKey reservekey(pwallet);
unsigned int nExtraNonce = 0; unsigned int nExtraNonce = 0;
while (fGenerateBitcoins) try { loop {
{ while (vNodes.empty())
if (fShutdown) MilliSleep(1000);
return;
while (vNodes.empty() || IsInitialBlockDownload())
{
Sleep(1000);
if (fShutdown)
return;
if (!fGenerateBitcoins)
return;
}
// //
// Create new block // 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(), printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
// //
// Pre-build hash buffers // Pre-build hash buffers
// //
@ -4627,19 +4605,14 @@ void static BitcoinMiner(CWallet *pwallet)
if (GetTime() - nLogTime > 30 * 60) if (GetTime() - nLogTime > 30 * 60)
{ {
nLogTime = GetTime(); 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 // Check for stop or if block needs to be rebuilt
if (fShutdown) boost::this_thread::interruption_point();
return;
if (!fGenerateBitcoins)
return;
if (fLimitProcessors && vnThreadsRunning[THREAD_MINER] > nLimitProcessors)
return;
if (vNodes.empty()) if (vNodes.empty())
break; break;
if (nBlockNonce >= 0xffff0000) if (nBlockNonce >= 0xffff0000)
@ -4659,57 +4632,35 @@ void static BitcoinMiner(CWallet *pwallet)
hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
} }
} }
} } }
} catch (boost::thread_interrupted)
void static ThreadBitcoinMiner(void* parg)
{
CWallet* pwallet = (CWallet*)parg;
try
{ {
vnThreadsRunning[THREAD_MINER]++; printf("BitcoinMiner terminated\n");
BitcoinMiner(pwallet); throw;
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]);
} }
void GenerateBitcoins(bool fGenerate, CWallet* pwallet) void GenerateBitcoins(bool fGenerate, CWallet* pwallet)
{ {
fGenerateBitcoins = fGenerate; static boost::thread_group* minerThreads = NULL;
nLimitProcessors = GetArg("-genproclimit", -1);
if (nLimitProcessors == 0)
fGenerateBitcoins = false;
fLimitProcessors = (nLimitProcessors != -1);
if (fGenerate) int nThreads = GetArg("-genproclimit", -1);
if (nThreads < 0)
nThreads = boost::thread::hardware_concurrency();
if (minerThreads != NULL)
{ {
int nProcessors = boost::thread::hardware_concurrency(); minerThreads->interrupt_all();
printf("%d processors\n", nProcessors); delete minerThreads;
if (nProcessors < 1) minerThreads = NULL;
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);
}
} }
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: // Amount compression:

6
src/main.h

@ -150,12 +150,8 @@ CBlockIndex* FindBlockByHeight(int nHeight);
bool ProcessMessages(CNode* pfrom); bool ProcessMessages(CNode* pfrom);
/** Send queued protocol messages to be sent to a give node */ /** Send queued protocol messages to be sent to a give node */
bool SendMessages(CNode* pto, bool fSendTrickle); 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 */ /** Run an instance of the script checking thread */
void ThreadScriptCheck(void* parg); void ThreadScriptCheck();
/** Stop the script checking threads */
void ThreadScriptCheckQuit();
/** Run the miner threads */ /** Run the miner threads */
void GenerateBitcoins(bool fGenerate, CWallet* pwallet); void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
/** Generate a new block, without valid proof-of-work */ /** Generate a new block, without valid proof-of-work */

399
src/net.cpp

@ -26,14 +26,6 @@ using namespace boost;
static const int MAX_OUTBOUND_CONNECTIONS = 8; 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); bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
@ -46,7 +38,6 @@ struct LocalServiceInfo {
// Global state variables // Global state variables
// //
bool fDiscover = true; bool fDiscover = true;
bool fUseUPnP = false;
uint64 nLocalServices = NODE_NETWORK; uint64 nLocalServices = NODE_NETWORK;
static CCriticalSection cs_mapLocalHost; static CCriticalSection cs_mapLocalHost;
static map<CNetAddr, LocalServiceInfo> mapLocalHost; static map<CNetAddr, LocalServiceInfo> mapLocalHost;
@ -54,7 +45,6 @@ static bool vfReachable[NET_MAX] = {};
static bool vfLimited[NET_MAX] = {}; static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL; static CNode* pnodeLocalHost = NULL;
uint64 nLocalHostNonce = 0; uint64 nLocalHostNonce = 0;
array<int, THREAD_MAX> vnThreadsRunning;
static std::vector<SOCKET> vhListenSocket; static std::vector<SOCKET> vhListenSocket;
CAddrMan addrman; CAddrMan addrman;
@ -156,8 +146,7 @@ bool RecvLine(SOCKET hSocket, string& strLine)
} }
else if (nBytes <= 0) else if (nBytes <= 0)
{ {
if (fShutdown) boost::this_thread::interruption_point();
return false;
if (nBytes < 0) if (nBytes < 0)
{ {
int nErr = WSAGetLastError(); int nErr = WSAGetLastError();
@ -165,7 +154,7 @@ bool RecvLine(SOCKET hSocket, string& strLine)
continue; continue;
if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS) if (nErr == WSAEWOULDBLOCK || nErr == WSAEINTR || nErr == WSAEINPROGRESS)
{ {
Sleep(10); MilliSleep(10);
continue; continue;
} }
} }
@ -754,32 +743,10 @@ void SocketSendData(CNode *pnode)
pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); 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<CNode*> vNodesDisconnected; static list<CNode*> vNodesDisconnected;
void ThreadSocketHandler2(void* parg) void ThreadSocketHandler()
{ {
printf("ThreadSocketHandler started\n");
unsigned int nPrevNodeCount = 0; unsigned int nPrevNodeCount = 0;
loop loop
{ {
@ -892,12 +859,10 @@ void ThreadSocketHandler2(void* parg)
} }
} }
vnThreadsRunning[THREAD_SOCKETHANDLER]--;
int nSelect = select(have_fds ? hSocketMax + 1 : 0, int nSelect = select(have_fds ? hSocketMax + 1 : 0,
&fdsetRecv, &fdsetSend, &fdsetError, &timeout); &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
vnThreadsRunning[THREAD_SOCKETHANDLER]++; boost::this_thread::interruption_point();
if (fShutdown)
return;
if (nSelect == SOCKET_ERROR) if (nSelect == SOCKET_ERROR)
{ {
if (have_fds) if (have_fds)
@ -909,7 +874,7 @@ void ThreadSocketHandler2(void* parg)
} }
FD_ZERO(&fdsetSend); FD_ZERO(&fdsetSend);
FD_ZERO(&fdsetError); 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) BOOST_FOREACH(CNode* pnode, vNodesCopy)
{ {
if (fShutdown) boost::this_thread::interruption_point();
return;
// //
// Receive // Receive
@ -1076,7 +1040,7 @@ void ThreadSocketHandler2(void* parg)
pnode->Release(); pnode->Release();
} }
Sleep(10); MilliSleep(10);
} }
} }
@ -1089,31 +1053,8 @@ void ThreadSocketHandler2(void* parg)
#ifdef USE_UPNP #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()); std::string port = strprintf("%u", GetListenPort());
const char * multicastif = 0; const char * multicastif = 0;
const char * minissdpdpath = 0; const char * minissdpdpath = 0;
@ -1154,33 +1095,9 @@ void ThreadMapPort2(void* parg)
} }
string strDesc = "Bitcoin " + FormatFullVersion(); 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) try {
printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", loop {
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
{
#ifndef UPNPDISCOVER_SUCCESS #ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */ /* miniupnpc 1.5 */
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, 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)); port.c_str(), port.c_str(), lanaddr, r, strupnperror(r));
else else
printf("UPnP Port Mapping successful.\n");; 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 { } else {
printf("No valid UPnP IGDs found\n"); printf("No valid UPnP IGDs found\n");
freeUPNPDevlist(devlist); devlist = 0; freeUPNPDevlist(devlist); devlist = 0;
if (r != 0) if (r != 0)
FreeUPNPUrls(&urls); 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)) if (upnp_thread) {
printf("Error: ThreadMapPort(ThreadMapPort) failed\n"); upnp_thread->interrupt();
upnp_thread->join();
delete upnp_thread;
}
upnp_thread = new boost::thread(boost::bind(&TraceThread<boost::function<void()> >, "upnp", &ThreadMapPort));
}
else if (upnp_thread) {
upnp_thread->interrupt();
upnp_thread->join();
delete upnp_thread;
upnp_thread = NULL;
} }
} }
#else #else
void MapPort() void MapPort(bool)
{ {
// Intentionally left blank. // Intentionally left blank.
} }
@ -1254,32 +1187,10 @@ static const char *strTestNetDNSSeed[][2] = {
{NULL, NULL} {NULL, NULL}
}; };
void ThreadDNSAddressSeed(void* parg) void ThreadDNSAddressSeed()
{
// 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)
{ {
static const char *(*strDNSSeed)[2] = fTestNet ? strTestNetDNSSeed : strMainNetDNSSeed; static const char *(*strDNSSeed)[2] = fTestNet ? strTestNetDNSSeed : strMainNetDNSSeed;
printf("ThreadDNSAddressSeed started\n");
int found = 0; int found = 0;
printf("Loading addresses from DNS seeds (could take a while)\n"); printf("Loading addresses from DNS seeds (could take a while)\n");
@ -1409,57 +1320,6 @@ void DumpAddresses()
addrman.size(), GetTimeMillis() - nStart); 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() void static ProcessOneShot()
{ {
string strDest; string strDest;
@ -1478,10 +1338,8 @@ void static ProcessOneShot()
} }
} }
void ThreadOpenConnections2(void* parg) void ThreadOpenConnections()
{ {
printf("ThreadOpenConnections started\n");
// Connect to specific addresses // Connect to specific addresses
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0)
{ {
@ -1494,12 +1352,10 @@ void ThreadOpenConnections2(void* parg)
OpenNetworkConnection(addr, NULL, strAddr.c_str()); OpenNetworkConnection(addr, NULL, strAddr.c_str());
for (int i = 0; i < 10 && i < nLoop; i++) for (int i = 0; i < 10 && i < nLoop; i++)
{ {
Sleep(500); MilliSleep(500);
if (fShutdown)
return;
} }
} }
Sleep(500); MilliSleep(500);
} }
} }
@ -1509,18 +1365,10 @@ void ThreadOpenConnections2(void* parg)
{ {
ProcessOneShot(); ProcessOneShot();
vnThreadsRunning[THREAD_OPENCONNECTIONS]--; MilliSleep(500);
Sleep(500);
vnThreadsRunning[THREAD_OPENCONNECTIONS]++;
if (fShutdown)
return;
vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
CSemaphoreGrant grant(*semOutbound); CSemaphoreGrant grant(*semOutbound);
vnThreadsRunning[THREAD_OPENCONNECTIONS]++; boost::this_thread::interruption_point();
if (fShutdown)
return;
// Add seed nodes if IRC isn't working // Add seed nodes if IRC isn't working
if (addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet) if (addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet)
@ -1600,38 +1448,15 @@ void ThreadOpenConnections2(void* parg)
} }
} }
void ThreadOpenAddedConnections(void* parg) void ThreadOpenAddedConnections()
{
// 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)
{ {
printf("ThreadOpenAddedConnections started\n");
{ {
LOCK(cs_vAddedNodes); LOCK(cs_vAddedNodes);
vAddedNodes = mapMultiArgs["-addnode"]; vAddedNodes = mapMultiArgs["-addnode"];
} }
if (HaveNameProxy()) { if (HaveNameProxy()) {
while(!fShutdown) { while(true) {
list<string> lAddresses(0); list<string> lAddresses(0);
{ {
LOCK(cs_vAddedNodes); LOCK(cs_vAddedNodes);
@ -1642,15 +1467,10 @@ void ThreadOpenAddedConnections2(void* parg)
CAddress addr; CAddress addr;
CSemaphoreGrant grant(*semOutbound); CSemaphoreGrant grant(*semOutbound);
OpenNetworkConnection(addr, &grant, strAddNode.c_str()); OpenNetworkConnection(addr, &grant, strAddNode.c_str());
Sleep(500); MilliSleep(500);
if (fShutdown)
return;
} }
vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--; MilliSleep(120000); // Retry every 2 minutes
Sleep(120000); // Retry every 2 minutes
vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++;
} }
return;
} }
for (unsigned int i = 0; true; i++) for (unsigned int i = 0; true; i++)
@ -1694,17 +1514,9 @@ void ThreadOpenAddedConnections2(void* parg)
{ {
CSemaphoreGrant grant(*semOutbound); CSemaphoreGrant grant(*semOutbound);
OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant);
Sleep(500); MilliSleep(500);
if (fShutdown)
return;
} }
if (fShutdown) MilliSleep(120000); // Retry every 2 minutes
return;
vnThreadsRunning[THREAD_ADDEDCONNECTIONS]--;
Sleep(120000); // Retry every 2 minutes
vnThreadsRunning[THREAD_ADDEDCONNECTIONS]++;
if (fShutdown)
return;
} }
} }
@ -1714,8 +1526,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
// //
// Initiate outbound network connection // Initiate outbound network connection
// //
if (fShutdown) boost::this_thread::interruption_point();
return false;
if (!strDest) if (!strDest)
if (IsLocal(addrConnect) || if (IsLocal(addrConnect) ||
FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) ||
@ -1724,11 +1535,9 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
if (strDest && FindNode(strDest)) if (strDest && FindNode(strDest))
return false; return false;
vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
CNode* pnode = ConnectNode(addrConnect, strDest); CNode* pnode = ConnectNode(addrConnect, strDest);
vnThreadsRunning[THREAD_OPENCONNECTIONS]++; boost::this_thread::interruption_point();
if (fShutdown)
return false;
if (!pnode) if (!pnode)
return false; return false;
if (grantOutbound) if (grantOutbound)
@ -1746,33 +1555,10 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
void ThreadMessageHandler()
void ThreadMessageHandler(void* parg)
{ {
// 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); SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
while (!fShutdown) while (true)
{ {
vector<CNode*> vNodesCopy; vector<CNode*> vNodesCopy;
{ {
@ -1798,8 +1584,7 @@ void ThreadMessageHandler2(void* parg)
if (!ProcessMessages(pnode)) if (!ProcessMessages(pnode))
pnode->CloseSocketDisconnect(); pnode->CloseSocketDisconnect();
} }
if (fShutdown) boost::this_thread::interruption_point();
return;
// Send messages // Send messages
{ {
@ -1807,8 +1592,7 @@ void ThreadMessageHandler2(void* parg)
if (lockSend) if (lockSend)
SendMessages(pnode, pnode == pnodeTrickle); SendMessages(pnode, pnode == pnodeTrickle);
} }
if (fShutdown) boost::this_thread::interruption_point();
return;
} }
{ {
@ -1817,16 +1601,7 @@ void ThreadMessageHandler2(void* parg)
pnode->Release(); pnode->Release();
} }
// Wait and allow messages to bunch up. MilliSleep(100);
// 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;
} }
} }
@ -1998,7 +1773,7 @@ void static Discover()
NewThread(ThreadGetMyExternalIP, NULL); NewThread(ThreadGetMyExternalIP, NULL);
} }
void StartNode(void* parg) void StartNode(boost::thread_group& threadGroup)
{ {
// Make this thread recognisable as the startup thread // Make this thread recognisable as the startup thread
RenameThread("bitcoin-start"); RenameThread("bitcoin-start");
@ -2021,69 +1796,39 @@ void StartNode(void* parg)
if (!GetBoolArg("-dnsseed", true)) if (!GetBoolArg("-dnsseed", true))
printf("DNS seeding disabled\n"); printf("DNS seeding disabled\n");
else else
if (!NewThread(ThreadDNSAddressSeed, NULL)) threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "dnsseed", &ThreadDNSAddressSeed));
printf("Error: NewThread(ThreadDNSAddressSeed) failed\n");
#ifdef USE_UPNP
// Map ports with UPnP // Map ports with UPnP
if (fUseUPnP) MapPort(GetBoolArg("-upnp", USE_UPNP));
MapPort(); #endif
// Send and receive from sockets, accept connections // Send and receive from sockets, accept connections
if (!NewThread(ThreadSocketHandler, NULL)) threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "net", &ThreadSocketHandler));
printf("Error: NewThread(ThreadSocketHandler) failed\n");
// Initiate outbound connections from -addnode // Initiate outbound connections from -addnode
if (!NewThread(ThreadOpenAddedConnections, NULL)) threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "addcon", &ThreadOpenAddedConnections));
printf("Error: NewThread(ThreadOpenAddedConnections) failed\n");
// Initiate outbound connections // Initiate outbound connections
if (!NewThread(ThreadOpenConnections, NULL)) threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "opencon", &ThreadOpenConnections));
printf("Error: NewThread(ThreadOpenConnections) failed\n");
// Process messages // Process messages
if (!NewThread(ThreadMessageHandler, NULL)) threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
printf("Error: NewThread(ThreadMessageHandler) failed\n");
// Dump network addresses // Dump network addresses
if (!NewThread(ThreadDumpAddress, NULL)) threadGroup.create_thread(boost::bind(&LoopForever<void (*)()>, "dumpaddr", &DumpAddresses, 10000));
printf("Error; NewThread(ThreadDumpAddress) failed\n");
} }
bool StopNode() bool StopNode()
{ {
printf("StopNode()\n"); printf("StopNode()\n");
fShutdown = true; GenerateBitcoins(false, NULL);
MapPort(false);
nTransactionsUpdated++; nTransactionsUpdated++;
int64 nStart = GetTime();
if (semOutbound) if (semOutbound)
for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++) for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
semOutbound->post(); semOutbound->post();
do MilliSleep(50);
{
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);
DumpAddresses(); DumpAddresses();
return true; return true;

25
src/net.h

@ -37,10 +37,10 @@ void AddressCurrentlyConnected(const CService& addr);
CNode* FindNode(const CNetAddr& ip); CNode* FindNode(const CNetAddr& ip);
CNode* FindNode(const CService& ip); CNode* FindNode(const CService& ip);
CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL, int64 nTimeout=0); CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL, int64 nTimeout=0);
void MapPort(); void MapPort(bool fUseUPnP);
unsigned short GetListenPort(); unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string())); bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string()));
void StartNode(void* parg); void StartNode(boost::thread_group& threadGroup);
bool StopNode(); bool StopNode();
void SocketSendData(CNode *pnode); void SocketSendData(CNode *pnode);
@ -69,30 +69,9 @@ void SetReachable(enum Network net, bool fFlag = true);
CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); 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 fDiscover;
extern bool fUseUPnP;
extern uint64 nLocalServices; extern uint64 nLocalServices;
extern uint64 nLocalHostNonce; extern uint64 nLocalHostNonce;
extern boost::array<int, THREAD_MAX> vnThreadsRunning;
extern CAddrMan addrman; extern CAddrMan addrman;
extern std::vector<CNode*> vNodes; extern std::vector<CNode*> vNodes;

20
src/qt/bitcoin.cpp

@ -11,6 +11,7 @@
#include "guiutil.h" #include "guiutil.h"
#include "guiconstants.h" #include "guiconstants.h"
#include "init.h" #include "init.h"
#include "util.h"
#include "ui_interface.h" #include "ui_interface.h"
#include "paymentserver.h" #include "paymentserver.h"
@ -87,11 +88,6 @@ static void InitMessage(const std::string &message)
printf("init message: %s\n", message.c_str()); printf("init message: %s\n", message.c_str());
} }
static void QueueShutdown()
{
QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
}
/* /*
Translate string to current locale using Qt. Translate string to current locale using Qt.
*/ */
@ -185,7 +181,6 @@ int main(int argc, char *argv[])
uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox); uiInterface.ThreadSafeMessageBox.connect(ThreadSafeMessageBox);
uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee); uiInterface.ThreadSafeAskFee.connect(ThreadSafeAskFee);
uiInterface.InitMessage.connect(InitMessage); uiInterface.InitMessage.connect(InitMessage);
uiInterface.QueueShutdown.connect(QueueShutdown);
uiInterface.Translate.connect(Translate); uiInterface.Translate.connect(Translate);
// Show help message immediately after parsing command-line options (for "-lang") and setting locale, // 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()) if (GUIUtil::GetStartOnSystemStartup())
GUIUtil::SetStartOnSystemStartup(true); GUIUtil::SetStartOnSystemStartup(true);
boost::thread_group threadGroup;
BitcoinGUI window; BitcoinGUI window;
guiref = &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 // 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; guiref = 0;
} }
// Shutdown the core and its threads, but don't exit Bitcoin-Qt here // Shutdown the core and its threads, but don't exit Bitcoin-Qt here
Shutdown(NULL); threadGroup.interrupt_all();
threadGroup.join_all();
Shutdown();
} }
else else
{ {

7
src/qt/bitcoingui.cpp

@ -24,6 +24,7 @@
#include "rpcconsole.h" #include "rpcconsole.h"
#include "ui_interface.h" #include "ui_interface.h"
#include "wallet.h" #include "wallet.h"
#include "init.h"
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
#include "macdockiconhandler.h" #include "macdockiconhandler.h"
@ -839,3 +840,9 @@ void BitcoinGUI::toggleHidden()
{ {
showNormalIfMinimized(true); showNormalIfMinimized(true);
} }
void BitcoinGUI::detectShutdown()
{
if (ShutdownRequested())
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
}

3
src/qt/bitcoingui.h

@ -199,6 +199,9 @@ private slots:
void showNormalIfMinimized(bool fToggleHidden = false); void showNormalIfMinimized(bool fToggleHidden = false);
/** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */ /** Simply calls showNormalIfMinimized(true) for use in SLOT() macro */
void toggleHidden(); void toggleHidden();
/** called by a timer to check if fRequestShutdown has been set **/
void detectShutdown();
}; };
#endif // BITCOINGUI_H #endif // BITCOINGUI_H

5
src/qt/optionsmodel.cpp

@ -219,9 +219,8 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
settings.setValue("fMinimizeToTray", fMinimizeToTray); settings.setValue("fMinimizeToTray", fMinimizeToTray);
break; break;
case MapPortUPnP: case MapPortUPnP:
fUseUPnP = value.toBool(); settings.setValue("fUseUPnP", value.toBool());
settings.setValue("fUseUPnP", fUseUPnP); MapPort(value.toBool());
MapPort();
break; break;
case MinimizeOnClose: case MinimizeOnClose:
fMinimizeOnClose = value.toBool(); fMinimizeOnClose = value.toBool();

2
src/rpcwallet.cpp

@ -1289,7 +1289,7 @@ void ThreadCleanWalletPassphrase(void* parg)
break; break;
LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime); LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
Sleep(nToSleep); MilliSleep(nToSleep);
ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime); ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
} while(1); } while(1);

24
src/test/bignum_tests.cpp

@ -128,51 +128,51 @@ BOOST_AUTO_TEST_CASE(bignum_SetCompact)
CBigNum num; CBigNum num;
num.SetCompact(0); num.SetCompact(0);
BOOST_CHECK_EQUAL(num.GetHex(), "0"); BOOST_CHECK_EQUAL(num.GetHex(), "0");
BOOST_CHECK_EQUAL(num.GetCompact(), 0); BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
num.SetCompact(0x00123456); num.SetCompact(0x00123456);
BOOST_CHECK_EQUAL(num.GetHex(), "0"); BOOST_CHECK_EQUAL(num.GetHex(), "0");
BOOST_CHECK_EQUAL(num.GetCompact(), 0); BOOST_CHECK_EQUAL(num.GetCompact(), 0U);
num.SetCompact(0x01123456); num.SetCompact(0x01123456);
BOOST_CHECK_EQUAL(num.GetHex(), "12"); 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 // Make sure that we don't generate compacts with the 0x00800000 bit set
num = 0x80; num = 0x80;
BOOST_CHECK_EQUAL(num.GetCompact(), 0x02008000); BOOST_CHECK_EQUAL(num.GetCompact(), 0x02008000U);
num.SetCompact(0x01fedcba); num.SetCompact(0x01fedcba);
BOOST_CHECK_EQUAL(num.GetHex(), "-7e"); BOOST_CHECK_EQUAL(num.GetHex(), "-7e");
BOOST_CHECK_EQUAL(num.GetCompact(), 0x01fe0000); BOOST_CHECK_EQUAL(num.GetCompact(), 0x01fe0000U);
num.SetCompact(0x02123456); num.SetCompact(0x02123456);
BOOST_CHECK_EQUAL(num.GetHex(), "1234"); BOOST_CHECK_EQUAL(num.GetHex(), "1234");
BOOST_CHECK_EQUAL(num.GetCompact(), 0x02123400); BOOST_CHECK_EQUAL(num.GetCompact(), 0x02123400U);
num.SetCompact(0x03123456); num.SetCompact(0x03123456);
BOOST_CHECK_EQUAL(num.GetHex(), "123456"); BOOST_CHECK_EQUAL(num.GetHex(), "123456");
BOOST_CHECK_EQUAL(num.GetCompact(), 0x03123456); BOOST_CHECK_EQUAL(num.GetCompact(), 0x03123456U);
num.SetCompact(0x04123456); num.SetCompact(0x04123456);
BOOST_CHECK_EQUAL(num.GetHex(), "12345600"); BOOST_CHECK_EQUAL(num.GetHex(), "12345600");
BOOST_CHECK_EQUAL(num.GetCompact(), 0x04123456); BOOST_CHECK_EQUAL(num.GetCompact(), 0x04123456U);
num.SetCompact(0x04923456); num.SetCompact(0x04923456);
BOOST_CHECK_EQUAL(num.GetHex(), "-12345600"); BOOST_CHECK_EQUAL(num.GetHex(), "-12345600");
BOOST_CHECK_EQUAL(num.GetCompact(), 0x04923456); BOOST_CHECK_EQUAL(num.GetCompact(), 0x04923456U);
num.SetCompact(0x05009234); num.SetCompact(0x05009234);
BOOST_CHECK_EQUAL(num.GetHex(), "92340000"); BOOST_CHECK_EQUAL(num.GetHex(), "92340000");
BOOST_CHECK_EQUAL(num.GetCompact(), 0x05009234); BOOST_CHECK_EQUAL(num.GetCompact(), 0x05009234U);
num.SetCompact(0x20123456); num.SetCompact(0x20123456);
BOOST_CHECK_EQUAL(num.GetHex(), "1234560000000000000000000000000000000000000000000000000000000000"); BOOST_CHECK_EQUAL(num.GetHex(), "1234560000000000000000000000000000000000000000000000000000000000");
BOOST_CHECK_EQUAL(num.GetCompact(), 0x20123456); BOOST_CHECK_EQUAL(num.GetCompact(), 0x20123456U);
num.SetCompact(0xff123456); num.SetCompact(0xff123456);
BOOST_CHECK_EQUAL(num.GetHex(), "123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); BOOST_CHECK_EQUAL(num.GetHex(), "123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
BOOST_CHECK_EQUAL(num.GetCompact(), 0xff123456); BOOST_CHECK_EQUAL(num.GetCompact(), 0xff123456U);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

4
src/test/multisig_tests.cpp

@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
CScript s; CScript s;
s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK_EQUAL(solutions.size(), 4); BOOST_CHECK_EQUAL(solutions.size(), 4U);
CTxDestination addr; CTxDestination addr;
BOOST_CHECK(!ExtractDestination(s, addr)); BOOST_CHECK(!ExtractDestination(s, addr));
BOOST_CHECK(IsMine(keystore, s)); BOOST_CHECK(IsMine(keystore, s));
@ -229,7 +229,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1)
CScript s; CScript s;
s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(Solver(s, whichType, solutions)); BOOST_CHECK(Solver(s, whichType, solutions));
BOOST_CHECK_EQUAL(solutions.size(), 4); BOOST_CHECK_EQUAL(solutions.size(), 4U);
vector<CTxDestination> addrs; vector<CTxDestination> addrs;
int nRequired; int nRequired;
BOOST_CHECK(ExtractDestinations(s, whichType, addrs, nRequired)); BOOST_CHECK(ExtractDestinations(s, whichType, addrs, nRequired));

4
src/test/script_P2SH_tests.cpp

@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2)); BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2));
BOOST_CHECK(txTo.AreInputsStandard(coins)); 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: // Make sure adding crap to the scriptSigs makes them non-standard:
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
@ -327,7 +327,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven); txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven);
BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); BOOST_CHECK(!txToNonStd.AreInputsStandard(coins));
BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11); BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11U);
txToNonStd.vin[0].scriptSig.clear(); txToNonStd.vin[0].scriptSig.clear();
BOOST_CHECK(!txToNonStd.AreInputsStandard(coins)); BOOST_CHECK(!txToNonStd.AreInputsStandard(coins));

4
src/test/serialize_tests.cpp

@ -29,13 +29,13 @@ BOOST_AUTO_TEST_CASE(varints)
// decode // decode
for (int i = 0; i < 100000; i++) { for (int i = 0; i < 100000; i++) {
int j; int j = -1;
ss >> VARINT(j); ss >> VARINT(j);
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
} }
for (uint64 i = 0; i < 100000000000ULL; i += 999999937) { for (uint64 i = 0; i < 100000000000ULL; i += 999999937) {
uint64 j; uint64 j = -1;
ss >> VARINT(j); ss >> VARINT(j);
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
} }

22
src/test/sigopcount_tests.cpp

@ -21,21 +21,21 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
{ {
// Test CScript::GetSigOpCount() // Test CScript::GetSigOpCount()
CScript s1; CScript s1;
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0); BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0U);
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0); BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0U);
uint160 dummy; uint160 dummy;
s1 << OP_1 << dummy << dummy << OP_2 << OP_CHECKMULTISIG; 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; s1 << OP_IF << OP_CHECKSIG << OP_ENDIF;
BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3); BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U);
BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21); BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U);
CScript p2sh; CScript p2sh;
p2sh.SetDestination(s1.GetID()); p2sh.SetDestination(s1.GetID());
CScript scriptSig; CScript scriptSig;
scriptSig << OP_0 << Serialize(s1); scriptSig << OP_0 << Serialize(s1);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3); BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U);
std::vector<CKey> keys; std::vector<CKey> keys;
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
@ -46,15 +46,15 @@ BOOST_AUTO_TEST_CASE(GetSigOpCount)
} }
CScript s2; CScript s2;
s2.SetMultisig(1, keys); s2.SetMultisig(1, keys);
BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3); BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U);
BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20); BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U);
p2sh.SetDestination(s2.GetID()); p2sh.SetDestination(s2.GetID());
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0); BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U);
BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0); BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U);
CScript scriptSig2; CScript scriptSig2;
scriptSig2 << OP_1 << dummy << dummy << Serialize(s2); 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() BOOST_AUTO_TEST_SUITE_END()

6
src/test/test_bitcoin.cpp

@ -17,6 +17,7 @@ extern void noui_connect();
struct TestingSetup { struct TestingSetup {
CCoinsViewDB *pcoinsdbview; CCoinsViewDB *pcoinsdbview;
boost::filesystem::path pathTemp; boost::filesystem::path pathTemp;
boost::thread_group threadGroup;
TestingSetup() { TestingSetup() {
fPrintToDebugger = true; // don't want to write to debug.log file fPrintToDebugger = true; // don't want to write to debug.log file
@ -35,11 +36,12 @@ struct TestingSetup {
RegisterWallet(pwalletMain); RegisterWallet(pwalletMain);
nScriptCheckThreads = 3; nScriptCheckThreads = 3;
for (int i=0; i < nScriptCheckThreads-1; i++) for (int i=0; i < nScriptCheckThreads-1; i++)
NewThread(ThreadScriptCheck, NULL); threadGroup.create_thread(&ThreadScriptCheck);
} }
~TestingSetup() ~TestingSetup()
{ {
ThreadScriptCheckQuit(); threadGroup.interrupt_all();
threadGroup.join_all();
delete pwalletMain; delete pwalletMain;
pwalletMain = NULL; pwalletMain = NULL;
delete pcoinsTip; delete pcoinsTip;

58
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<void (*)()>, "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<void()> f = boost::bind(&CountWithArg, 11);
threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "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<void (*)()>, "count11", &Count));
threadGroup.join_all();
BOOST_CHECK_EQUAL(nCounter, 1);
nCounter = 0;
}
BOOST_AUTO_TEST_CASE(util_threadtrace2)
{
boost::thread_group threadGroup;
boost::function<void()> f = boost::bind(&CountWithArg, 11);
threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "count11", f));
threadGroup.join_all();
BOOST_CHECK_EQUAL(nCounter, 11);
nCounter = 0;
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

30
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 // 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( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_GT(nValueRet, 34 * CENT); // but should get more than 34 cents 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 // 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( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT); 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. // 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( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK(nValueRet == 8 * CENT); 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) // 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( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT); 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 // now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin
empty_wallet(); 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 // 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( 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(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 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 // 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( 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(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 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 // 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( 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(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 // now try making 11 cents. we should get 5+6
BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT); 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 // check that the smallest bigger coin is used
add_coin( 1*COIN); 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 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( 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(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( 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(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 the wallet and start again, now with fractions of a cent, to test sub-cent change avoidance
empty_wallet(); 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( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount 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), // 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 // we need to try finding an exact subset anyway
@ -220,7 +220,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(1111 * CENT); add_coin(1111 * CENT);
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); 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(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) // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
empty_wallet(); empty_wallet();
@ -230,7 +230,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(1111 * CENT); add_coin(1111 * CENT);
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); 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(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 // test avoiding sub-cent change
empty_wallet(); empty_wallet();
@ -241,12 +241,12 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// trying to make 1.0001 from these three coins // trying to make 1.0001 from these three coins
BOOST_CHECK( wallet.SelectCoinsMinConf(1.0001 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); 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(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 // 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( 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(nValueRet, 1.01 * COIN); // we should get 1 + 0.01
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// test randomness // test randomness
{ {

6
src/txdb.cpp

@ -115,12 +115,13 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) {
pcursor->SeekToFirst(); pcursor->SeekToFirst();
while (pcursor->Valid()) { while (pcursor->Valid()) {
boost::this_thread::interruption_point();
try { try {
leveldb::Slice slKey = pcursor->key(); leveldb::Slice slKey = pcursor->key();
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
char chType; char chType;
ssKey >> chType; ssKey >> chType;
if (chType == 'c' && !fRequestShutdown) { if (chType == 'c') {
leveldb::Slice slValue = pcursor->value(); leveldb::Slice slValue = pcursor->value();
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CCoins coins; CCoins coins;
@ -178,12 +179,13 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
// Load mapBlockIndex // Load mapBlockIndex
while (pcursor->Valid()) { while (pcursor->Valid()) {
boost::this_thread::interruption_point();
try { try {
leveldb::Slice slKey = pcursor->key(); leveldb::Slice slKey = pcursor->key();
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
char chType; char chType;
ssKey >> chType; ssKey >> chType;
if (chType == 'b' && !fRequestShutdown) { if (chType == 'b') {
leveldb::Slice slValue = pcursor->value(); leveldb::Slice slValue = pcursor->value();
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
CDiskBlockIndex diskindex; CDiskBlockIndex diskindex;

3
src/ui_interface.h

@ -79,9 +79,6 @@ public:
/** Progress message during initialization. */ /** Progress message during initialization. */
boost::signals2::signal<void (const std::string &message)> InitMessage; boost::signals2::signal<void (const std::string &message)> InitMessage;
/** Initiate client shutdown. */
boost::signals2::signal<void ()> QueueShutdown;
/** Translate a message to the native language of the user. */ /** Translate a message to the native language of the user. */
boost::signals2::signal<std::string (const char* psz)> Translate; boost::signals2::signal<std::string (const char* psz)> Translate;

11
src/util.cpp

@ -73,8 +73,6 @@ bool fDebug = false;
bool fDebugNet = false; bool fDebugNet = false;
bool fPrintToConsole = false; bool fPrintToConsole = false;
bool fPrintToDebugger = false; bool fPrintToDebugger = false;
volatile bool fRequestShutdown = false;
bool fShutdown = false;
bool fDaemon = false; bool fDaemon = false;
bool fServer = false; bool fServer = false;
bool fCommandLine = false; bool fCommandLine = false;
@ -1431,9 +1429,12 @@ void RenameThread(const char* name)
// removed. // removed.
pthread_set_name_np(pthread_self(), name); 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) && defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
// #elif defined(MAC_OSX)
// pthread_setname_np(name); // pthread_setname_np is XCode 10.6-and-later
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
pthread_setname_np(name);
#endif
#else #else
// Prevent warnings for unused parameters... // Prevent warnings for unused parameters...

74
src/util.h

@ -15,6 +15,8 @@
typedef int pid_t; /* define for Windows compatibility */ typedef int pid_t; /* define for Windows compatibility */
#endif #endif
#include <map> #include <map>
#include <list>
#include <utility>
#include <vector> #include <vector>
#include <string> #include <string>
@ -98,13 +100,16 @@ T* alignup(T* p)
#endif #endif
#else #else
#define MAX_PATH 1024 #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. #if BOOST_VERSION >= 105000
So we clamp our sleeps here to 10 years and hope that boost is fixed by 2028.*/ boost::this_thread::sleep_for(boost::chrono::milliseconds(n));
boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n>315576000000LL?315576000000LL:n)); #else
} boost::this_thread::sleep(boost::posix_time::milliseconds(n));
#endif #endif
}
/* This GNU C extension enables the compiler to check the format string against the parameters provided. /* 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. * 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 fDebugNet;
extern bool fPrintToConsole; extern bool fPrintToConsole;
extern bool fPrintToDebugger; extern bool fPrintToDebugger;
extern volatile bool fRequestShutdown;
extern bool fShutdown;
extern bool fDaemon; extern bool fDaemon;
extern bool fServer; extern bool fServer;
extern bool fCommandLine; extern bool fCommandLine;
@ -522,5 +525,60 @@ inline uint32_t ByteReverse(uint32_t value)
return (value<<16) | (value>>16); 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<void (*)()>, "dumpaddr", &DumpAddresses, 10000));
// or maybe:
// boost::function<void()> f = boost::bind(&FunctionWithArg, argument);
// threadGroup.create_thread(boost::bind(&LoopForever<boost::function<void()> >, "nothing", f, milliseconds));
template <typename Callable> 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 <typename Callable> 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

1
src/wallet.cpp

@ -1383,7 +1383,6 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
return nLoadWalletRet; return nLoadWalletRet;
fFirstRunRet = !vchDefaultKey.IsValid(); fFirstRunRet = !vchDefaultKey.IsValid();
NewThread(ThreadFlushWalletDB, &strWalletFile);
return DB_LOAD_OK; return DB_LOAD_OK;
} }

3
src/wallet.h

@ -327,8 +327,7 @@ public:
~CReserveKey() ~CReserveKey()
{ {
if (!fShutdown) ReturnKey();
ReturnKey();
} }
void ReturnKey(); void ReturnKey();

20
src/walletdb.cpp

@ -451,8 +451,10 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
} }
pcursor->close(); pcursor->close();
} }
catch (...) catch (boost::thread_interrupted) {
{ throw;
}
catch (...) {
result = DB_CORRUPT; result = DB_CORRUPT;
} }
@ -482,12 +484,11 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet)
return result; return result;
} }
void ThreadFlushWalletDB(void* parg) void ThreadFlushWalletDB(const string& strFile)
{ {
// Make this thread recognisable as the wallet flushing thread // Make this thread recognisable as the wallet flushing thread
RenameThread("bitcoin-wallet"); RenameThread("bitcoin-wallet");
const string& strFile = ((const string*)parg)[0];
static bool fOneThread; static bool fOneThread;
if (fOneThread) if (fOneThread)
return; return;
@ -498,9 +499,9 @@ void ThreadFlushWalletDB(void* parg)
unsigned int nLastSeen = nWalletDBUpdated; unsigned int nLastSeen = nWalletDBUpdated;
unsigned int nLastFlushed = nWalletDBUpdated; unsigned int nLastFlushed = nWalletDBUpdated;
int64 nLastWalletUpdate = GetTime(); int64 nLastWalletUpdate = GetTime();
while (!fShutdown) while (true)
{ {
Sleep(500); MilliSleep(500);
if (nLastSeen != nWalletDBUpdated) if (nLastSeen != nWalletDBUpdated)
{ {
@ -522,8 +523,9 @@ void ThreadFlushWalletDB(void* parg)
mi++; mi++;
} }
if (nRefCount == 0 && !fShutdown) if (nRefCount == 0)
{ {
boost::this_thread::interruption_point();
map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile); map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
if (mi != bitdb.mapFileUseCount.end()) if (mi != bitdb.mapFileUseCount.end())
{ {
@ -548,7 +550,7 @@ bool BackupWallet(const CWallet& wallet, const string& strDest)
{ {
if (!wallet.fFileBacked) if (!wallet.fFileBacked)
return false; return false;
while (!fShutdown) while (true)
{ {
{ {
LOCK(bitdb.cs_db); LOCK(bitdb.cs_db);
@ -579,7 +581,7 @@ bool BackupWallet(const CWallet& wallet, const string& strDest)
} }
} }
} }
Sleep(100); MilliSleep(100);
} }
return false; return false;
} }

Loading…
Cancel
Save