mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-03 09:56:38 -05:00
Merge pull request #2357 from gavinandresen/shutdowncleanup
Thread / shutdown cleanup
This commit is contained in:
commit
a0a437c86a
31 changed files with 529 additions and 810 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(rpc_io_service == NULL);
|
||||||
|
rpc_io_service = new asio::io_service();
|
||||||
|
rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23);
|
||||||
|
|
||||||
const bool fUseSSL = GetBoolArg("-rpcssl");
|
const bool fUseSSL = GetBoolArg("-rpcssl");
|
||||||
|
|
||||||
asio::io_service io_service;
|
|
||||||
|
|
||||||
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 ¶ms) const
|
json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const
|
||||||
|
@ -1277,13 +1221,14 @@ int CommandLineRPC(int argc, char *argv[])
|
||||||
strPrint = write_string(result, true);
|
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 (...) {
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -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
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
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);
|
||||||
|
|
||||||
|
|
||||||
|
|
233
src/init.cpp
233
src/init.cpp
|
@ -40,55 +40,54 @@ 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;
|
|
||||||
|
|
||||||
// Make this thread recognisable as the shutdown thread
|
|
||||||
RenameThread("bitcoin-shutoff");
|
|
||||||
|
|
||||||
bool fFirstThread = false;
|
|
||||||
{
|
|
||||||
TRY_LOCK(cs_Shutdown, lockShutdown);
|
TRY_LOCK(cs_Shutdown, lockShutdown);
|
||||||
if (lockShutdown)
|
if (!lockShutdown) return;
|
||||||
{
|
|
||||||
fFirstThread = !fTaken;
|
RenameThread("bitcoin-shutoff");
|
||||||
fTaken = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static bool fExit;
|
|
||||||
if (fFirstThread)
|
|
||||||
{
|
|
||||||
fShutdown = true;
|
|
||||||
fRequestShutdown = true;
|
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
|
StopRPCThreads();
|
||||||
bitdb.Flush(false);
|
bitdb.Flush(false);
|
||||||
{
|
|
||||||
LOCK(cs_main);
|
|
||||||
ThreadScriptCheckQuit();
|
|
||||||
}
|
|
||||||
StopNode();
|
StopNode();
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
@ -96,29 +95,27 @@ void Shutdown(void* parg)
|
||||||
pblocktree->Flush();
|
pblocktree->Flush();
|
||||||
if (pcoinsTip)
|
if (pcoinsTip)
|
||||||
pcoinsTip->Flush();
|
pcoinsTip->Flush();
|
||||||
delete pcoinsTip;
|
delete pcoinsTip; pcoinsTip = NULL;
|
||||||
delete pcoinsdbview;
|
delete pcoinsdbview; pcoinsdbview = NULL;
|
||||||
delete pblocktree;
|
delete pblocktree; pblocktree = NULL;
|
||||||
}
|
}
|
||||||
bitdb.Flush(true);
|
bitdb.Flush(true);
|
||||||
boost::filesystem::remove(GetPidFile());
|
boost::filesystem::remove(GetPidFile());
|
||||||
UnregisterWallet(pwalletMain);
|
UnregisterWallet(pwalletMain);
|
||||||
delete 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
|
|
||||||
|
//
|
||||||
|
// Signal handlers are very limited in what they are allowed to do, so:
|
||||||
|
//
|
||||||
|
void DetectShutdownThread(boost::thread_group* threadGroup)
|
||||||
{
|
{
|
||||||
while (!fExit)
|
// Tell the main threads to shutdown.
|
||||||
Sleep(500);
|
while (!fRequestShutdown)
|
||||||
Sleep(100);
|
{
|
||||||
ExitThread(0);
|
MilliSleep(200);
|
||||||
|
if (fRequestShutdown)
|
||||||
|
threadGroup->interrupt_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
143
src/main.cpp
143
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;
|
printf("BitcoinMiner terminated\n");
|
||||||
try
|
throw;
|
||||||
{
|
|
||||||
vnThreadsRunning[THREAD_MINER]++;
|
|
||||||
BitcoinMiner(pwallet);
|
|
||||||
vnThreadsRunning[THREAD_MINER]--;
|
|
||||||
}
|
}
|
||||||
catch (std::exception& e) {
|
|
||||||
vnThreadsRunning[THREAD_MINER]--;
|
|
||||||
PrintException(&e, "ThreadBitcoinMiner()");
|
|
||||||
} catch (...) {
|
|
||||||
vnThreadsRunning[THREAD_MINER]--;
|
|
||||||
PrintException(NULL, "ThreadBitcoinMiner()");
|
|
||||||
}
|
}
|
||||||
nHPSTimerStart = 0;
|
|
||||||
if (vnThreadsRunning[THREAD_MINER] == 0)
|
|
||||||
dHashesPerSec = 0;
|
|
||||||
printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINER]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
397
src/net.cpp
397
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",
|
|
||||||
port.c_str(), port.c_str(), lanaddr, r, strupnperror(r));
|
|
||||||
else
|
|
||||||
printf("UPnP Port Mapping successful.\n");
|
|
||||||
int i = 1;
|
|
||||||
loop {
|
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
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;
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
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...
|
||||||
|
|
76
src/util.h
76
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,14 +100,17 @@ T* alignup(T* p)
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#define MAX_PATH 1024
|
#define MAX_PATH 1024
|
||||||
inline void Sleep(int64 n)
|
|
||||||
{
|
|
||||||
/*Boost has a year 2038 problem— if the request sleep time is past epoch+2^31 seconds the sleep returns instantly.
|
|
||||||
So we clamp our sleeps here to 10 years and hope that boost is fixed by 2028.*/
|
|
||||||
boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(n>315576000000LL?315576000000LL:n));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
inline void MilliSleep(int64 n)
|
||||||
|
{
|
||||||
|
#if BOOST_VERSION >= 105000
|
||||||
|
boost::this_thread::sleep_for(boost::chrono::milliseconds(n));
|
||||||
|
#else
|
||||||
|
boost::this_thread::sleep(boost::posix_time::milliseconds(n));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* This GNU C extension enables the compiler to check the format string against the parameters provided.
|
/* 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.
|
||||||
* Parameters count from 1.
|
* Parameters count from 1.
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -327,7 +327,6 @@ public:
|
||||||
|
|
||||||
~CReserveKey()
|
~CReserveKey()
|
||||||
{
|
{
|
||||||
if (!fShutdown)
|
|
||||||
ReturnKey();
|
ReturnKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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…
Add table
Reference in a new issue