From d692e5194b0369461c22960002451a507951bb88 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Mon, 24 Feb 2014 12:33:41 +0100 Subject: [PATCH 1/9] Encapsulate rpc server's client stream. Add a dummy 'game_waitforblock' method that only sleeps for now, and encapsulate the RPC server's client streams into a new class that can be used in the future to persist the streams in new threads for asynchronous RPC calls. Conflicts: src/bitcoinrpc.h src/namecoin.cpp --- src/bitcoinrpc.cpp | 106 +++++++++++++++++++++++++++++++++++++-------- src/bitcoinrpc.h | 4 ++ src/namecoin.cpp | 2 - 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index a78556a2e..d670cf967 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -45,8 +45,6 @@ using namespace boost::asio; using namespace json_spirit; void ThreadRPCServer2(void* parg); -typedef Value(*rpcfn_type)(const Array& params, bool fHelp); -extern map mapCallTable; Value sendtoaddress(const Array& params, bool fHelp); int64 nWalletUnlockTime; @@ -3186,6 +3184,10 @@ string pAllowInSafeMode[] = }; set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); +/* Methods that will be called in a new thread and can block waiting for + some condition without hurting the RPC server performance. */ +set setCallAsync; + @@ -3486,6 +3488,74 @@ class SSLIOStreamDevice : public iostreams::device { }; #endif +/** + * Class encapsulating the state necessary for writing to a client connection. + * This is used to hold the state for async method calls. + */ +class ClientConnectionOutput +{ + +private: + + /* The stream for outputting. */ +#ifdef USE_SSL + SSLStream* sslStream; + SSLIOStreamDevice* d; + iostreams::stream* stream; +#else + ip::tcp::iostream* stream; +#endif + +public: + + /* Basic constructor. */ +#ifdef USE_SSL + inline ClientConnectionOutput (asio::io_service& io, ssl::context& c, + bool fUseSSL) + : sslStream(new SSLStream (io, c)), + d(new SSLIOStreamDevice (*sslStream, fUseSSL)), + stream(new iostreams::stream (*d)) + {} +#else + inline ClientConnectionOutput () + : stream(new ip::tcp::iostream ()) + {} +#endif + + /* Destructor freeing everything. */ + inline ~ClientConnectionOutput () + { + delete stream; +#ifdef USE_SSL + delete d; + delete sslStream; +#endif + } + + /* Wait for incoming connection. */ + inline void + waitForConnection (ip::tcp::acceptor& acc, ip::tcp::endpoint& peer) + { +#ifdef USE_SSL + acc.accept(sslStream->lowest_layer(), peer); +#else + acc.accept(*stream->rdbuf(), peer); +#endif + } + + /* Return the stream held. */ +#ifdef USE_SSL + inline iostreams::stream& +#else + inline ip::tcp::iostream& +#endif + getStream () + { + return *stream; + } + +}; + void ThreadRPCServer(void* parg) { IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg)); @@ -3561,20 +3631,14 @@ void ThreadRPCServer2(void* parg) { // Accept connection #ifdef USE_SSL - SSLStream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream stream(d); + ClientConnectionOutput out(io_service, context, fUseSSL); #else - ip::tcp::iostream stream; + ClientConnectionOutput out; #endif ip::tcp::endpoint peer; vnThreadsRunning[4]--; -#ifdef USE_SSL - acceptor.accept(sslStream.lowest_layer(), peer); -#else - acceptor.accept(*stream.rdbuf(), peer); -#endif + out.waitForConnection (acceptor, peer); vnThreadsRunning[4]++; if (fShutdown) return; @@ -3584,14 +3648,16 @@ void ThreadRPCServer2(void* parg) { // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. if (!fUseSSL) - stream << HTTPReply(403, "") << std::flush; + out.getStream() << HTTPReply(403, "") << std::flush; continue; } map mapHeaders; string strRequest; - boost::thread api_caller(ReadHTTP, boost::ref(stream), boost::ref(mapHeaders), boost::ref(strRequest)); + boost::thread api_caller(ReadHTTP, boost::ref(out.getStream()), + boost::ref(mapHeaders), + boost::ref(strRequest)); if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) { // Timed out: acceptor.cancel(); @@ -3602,7 +3668,7 @@ void ThreadRPCServer2(void* parg) // Check authorization if (mapHeaders.count("authorization") == 0) { - stream << HTTPReply(401, "") << std::flush; + out.getStream() << HTTPReply(401, "") << std::flush; continue; } if (!HTTPAuthorized(mapHeaders)) @@ -3611,7 +3677,7 @@ void ThreadRPCServer2(void* parg) if (mapArgs["-rpcpassword"].size() < 15) Sleep(50); - stream << HTTPReply(401, "") << std::flush; + out.getStream() << HTTPReply(401, "") << std::flush; printf("ThreadRPCServer incorrect password attempt\n"); continue; } @@ -3665,20 +3731,22 @@ void ThreadRPCServer2(void* parg) // Send reply string strReply = JSONRPCReply(result, Value::null, id); - stream << HTTPReply(200, strReply) << std::flush; + out.getStream() << HTTPReply(200, strReply) << std::flush; } catch (std::exception& e) { - ErrorReply(stream, JSONRPCError(RPC_MISC_ERROR, e.what()), id); + ErrorReply(out.getStream(), + JSONRPCError(RPC_MISC_ERROR, e.what()), id); } } catch (Object& objError) { - ErrorReply(stream, objError, id); + ErrorReply(out.getStream(), objError, id); } catch (std::exception& e) { - ErrorReply(stream, JSONRPCError(RPC_PARSE_ERROR, e.what()), id); + ErrorReply(out.getStream(), + JSONRPCError(RPC_PARSE_ERROR, e.what()), id); } } } diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index 742e5c5c6..a9a1d4398 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -31,6 +31,10 @@ void RPCTypeCheck(const json_spirit::Object& o, extern std::string HelpRequiringPassphrase(); extern void EnsureWalletIsUnlocked(); +typedef json_spirit::Value(*rpcfn_type)(const json_spirit::Array& params, bool fHelp); +extern std::map mapCallTable; +extern std::set setCallAsync; + // Bitcoin RPC error codes enum RPCErrorCode { diff --git a/src/namecoin.cpp b/src/namecoin.cpp index 3166a0051..4b6ff06b0 100644 --- a/src/namecoin.cpp +++ b/src/namecoin.cpp @@ -21,8 +21,6 @@ using namespace std; using namespace json_spirit; static const bool NAME_DEBUG = false; -typedef Value(*rpcfn_type)(const Array& params, bool fHelp); -extern map mapCallTable; extern int64 AmountFromValue(const Value& value); extern Object JSONRPCError(int code, const string& message); template void ConvertTo(Value& value, bool fAllowNull=false); From 490b4c17620f98144222352f4fdcbd08155f7678 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Mon, 24 Feb 2014 13:57:55 +0100 Subject: [PATCH 2/9] Implement async RPC calls. Implement async RPC method calls that spawn a new thread waiting to finish them. Conflicts: src/namecoin.cpp --- src/bitcoinrpc.cpp | 184 +++++++++++++++++++++++++++++++-------------- src/bitcoinrpc.h | 3 + src/namecoin.cpp | 2 + 3 files changed, 134 insertions(+), 55 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index d670cf967..d5ea753ae 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -17,6 +17,10 @@ #include #include #include +#include + +#include +#include #ifdef USE_SSL #include @@ -3499,62 +3503,91 @@ class ClientConnectionOutput /* The stream for outputting. */ #ifdef USE_SSL - SSLStream* sslStream; - SSLIOStreamDevice* d; - iostreams::stream* stream; + SSLStream* sslStream; + SSLIOStreamDevice* d; + iostreams::stream* stream; #else - ip::tcp::iostream* stream; + ip::tcp::iostream* stream; #endif public: - /* Basic constructor. */ + /* Basic constructor. */ #ifdef USE_SSL - inline ClientConnectionOutput (asio::io_service& io, ssl::context& c, - bool fUseSSL) - : sslStream(new SSLStream (io, c)), - d(new SSLIOStreamDevice (*sslStream, fUseSSL)), - stream(new iostreams::stream (*d)) - {} + inline ClientConnectionOutput (asio::io_service& io, ssl::context& c, + bool fUseSSL) + : sslStream(new SSLStream (io, c)), + d(new SSLIOStreamDevice (*sslStream, fUseSSL)), + stream(new iostreams::stream (*d)) + {} #else - inline ClientConnectionOutput () - : stream(new ip::tcp::iostream ()) - {} + inline ClientConnectionOutput () + : stream(new ip::tcp::iostream ()) + {} #endif - /* Destructor freeing everything. */ - inline ~ClientConnectionOutput () - { - delete stream; + /* Destructor freeing everything. */ + inline ~ClientConnectionOutput () + { + delete stream; #ifdef USE_SSL - delete d; - delete sslStream; + delete d; + delete sslStream; #endif - } + } - /* Wait for incoming connection. */ - inline void - waitForConnection (ip::tcp::acceptor& acc, ip::tcp::endpoint& peer) - { + /* Wait for incoming connection. */ + inline void + waitForConnection (ip::tcp::acceptor& acc, ip::tcp::endpoint& peer) + { #ifdef USE_SSL - acc.accept(sslStream->lowest_layer(), peer); + acc.accept(sslStream->lowest_layer(), peer); #else - acc.accept(*stream->rdbuf(), peer); + acc.accept(*stream->rdbuf(), peer); #endif - } + } - /* Return the stream held. */ + /* Return the stream held. */ #ifdef USE_SSL - inline iostreams::stream& + inline iostreams::stream& #else - inline ip::tcp::iostream& + inline ip::tcp::iostream& #endif - getStream () + getStream () + { + return *stream; + } + +}; + +/* Execute an RPC call, can be used as thread object for async calls. */ +static void +ExecuteRpcCall (ClientConnectionOutput* out, rpcfn_type method, + json_spirit::Array params, json_spirit::Value id) +{ + try { - return *stream; + // Execute + Value result = method (params, false); + + // Send reply + string strReply = JSONRPCReply (result, json_spirit::Value::null, id); + out->getStream () << HTTPReply (200, strReply) << std::flush; + } + catch (const boost::thread_interrupted& e) + { + ErrorReply (out->getStream (), + JSONRPCError (RPC_ASYNC_INTERRUPT, + "async method interrupted"), id); + } + catch (const std::exception& e) + { + ErrorReply (out->getStream (), + JSONRPCError (RPC_MISC_ERROR, e.what ()), id); } -}; + delete out; +} void ThreadRPCServer(void* parg) { @@ -3627,35 +3660,78 @@ void ThreadRPCServer2(void* parg) throw runtime_error("-rpcssl=1, but namecoin compiled without full openssl libraries."); #endif + // Threads running async methods at the moment. + typedef std::list ThreadList; + ThreadList asyncThreads; + loop { + // Clean up async threads. + printf("Trying to clean up %d async RPC call threads...\n", + asyncThreads.size()); + for (ThreadList::iterator i = asyncThreads.begin(); + i != asyncThreads.end(); ) + { + if ((*i)->timed_join(boost::posix_time::seconds(0))) + { + printf("Async RPC call thread finished.\n"); + delete *i; + i = asyncThreads.erase (i); + } + else + { + /* Explicitly increment iterator here in case we didn't + delete the element above. */ + ++i; + } + } + // Accept connection + std::auto_ptr out; #ifdef USE_SSL - ClientConnectionOutput out(io_service, context, fUseSSL); + out.reset(new ClientConnectionOutput(io_service, context, fUseSSL)); #else - ClientConnectionOutput out; + out.reset(new ClientConnectionOutput()); #endif ip::tcp::endpoint peer; vnThreadsRunning[4]--; - out.waitForConnection (acceptor, peer); + out->waitForConnection (acceptor, peer); vnThreadsRunning[4]++; if (fShutdown) + { + printf("Waiting for %d async RPC call threads to finish...\n", + asyncThreads.size()); + + for (ThreadList::iterator i = asyncThreads.begin(); + i != asyncThreads.end(); ++i) + { + (*i)->interrupt(); + } + + for (ThreadList::iterator i = asyncThreads.begin(); + i != asyncThreads.end(); ++i) + { + (*i)->join(); + delete *i; + } + return; + } // Restrict callers by IP if (!ClientAllowed(peer.address().to_string())) { // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. if (!fUseSSL) - out.getStream() << HTTPReply(403, "") << std::flush; + out->getStream() << HTTPReply(403, "") << std::flush; continue; } map mapHeaders; string strRequest; - boost::thread api_caller(ReadHTTP, boost::ref(out.getStream()), + boost::thread api_caller(ReadHTTP, boost::ref(out->getStream()), boost::ref(mapHeaders), boost::ref(strRequest)); if (!api_caller.timed_join(boost::posix_time::seconds(GetArg("-rpctimeout", 30)))) @@ -3668,7 +3744,7 @@ void ThreadRPCServer2(void* parg) // Check authorization if (mapHeaders.count("authorization") == 0) { - out.getStream() << HTTPReply(401, "") << std::flush; + out->getStream() << HTTPReply(401, "") << std::flush; continue; } if (!HTTPAuthorized(mapHeaders)) @@ -3677,7 +3753,7 @@ void ThreadRPCServer2(void* parg) if (mapArgs["-rpcpassword"].size() < 15) Sleep(50); - out.getStream() << HTTPReply(401, "") << std::flush; + out->getStream() << HTTPReply(401, "") << std::flush; printf("ThreadRPCServer incorrect password attempt\n"); continue; } @@ -3724,28 +3800,26 @@ void ThreadRPCServer2(void* parg) if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); - try - { - // Execute - Value result = (*(*mi).second)(params, false); - - // Send reply - string strReply = JSONRPCReply(result, Value::null, id); - out.getStream() << HTTPReply(200, strReply) << std::flush; - } - catch (std::exception& e) + // Check for asynchronous execution and call the method. + const bool async = (setCallAsync.count(strMethod) > 0); + if (!async) + ExecuteRpcCall(out.release(), (*mi).second, params, id); + else { - ErrorReply(out.getStream(), - JSONRPCError(RPC_MISC_ERROR, e.what()), id); + std::auto_ptr runner; + runner.reset (new boost::thread (&ExecuteRpcCall, + out.release(), + (*mi).second, params, id)); + asyncThreads.push_back (runner.release()); } } catch (Object& objError) { - ErrorReply(out.getStream(), objError, id); + ErrorReply(out->getStream(), objError, id); } catch (std::exception& e) { - ErrorReply(out.getStream(), + ErrorReply(out->getStream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), id); } } diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index a9a1d4398..18954b249 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -69,6 +69,9 @@ enum RPCErrorCode RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.) RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked + + // Async method call interrupted. + RPC_ASYNC_INTERRUPT = -100, }; #endif diff --git a/src/namecoin.cpp b/src/namecoin.cpp index 4b6ff06b0..3809fcfb0 100644 --- a/src/namecoin.cpp +++ b/src/namecoin.cpp @@ -17,6 +17,8 @@ #include "json/json_spirit_utils.h" #include +#include + using namespace std; using namespace json_spirit; From 59c7c949e837a406697085e4ef00031eefa67b55 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Mon, 24 Feb 2014 14:05:27 +0100 Subject: [PATCH 3/9] Add comment about fShutdown in RPC thread. --- src/bitcoinrpc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index d5ea753ae..5da3930b0 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -3698,6 +3698,9 @@ void ThreadRPCServer2(void* parg) vnThreadsRunning[4]--; out->waitForConnection (acceptor, peer); vnThreadsRunning[4]++; + + /* Note: This isn't usually called since the thread blocks + in the routine above until the program terminates. */ if (fShutdown) { printf("Waiting for %d async RPC call threads to finish...\n", From e9bc87455d815cce8ec00dfa71419f43988be4d2 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Mon, 24 Feb 2014 21:12:11 +0100 Subject: [PATCH 4/9] Move check for RPC server shutdown. Move the check for RPC server shutdown above accept call, so that it does execute when stop() was called. --- src/bitcoinrpc.cpp | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 5da3930b0..445569d3e 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -3666,6 +3666,29 @@ void ThreadRPCServer2(void* parg) loop { + /* Shut down. Do this here before we block again in the accept call + below, when the last command was "stop", it can exit now. */ + if (fShutdown) + { + printf("Waiting for %d async RPC call threads to finish...\n", + asyncThreads.size()); + + for (ThreadList::iterator i = asyncThreads.begin(); + i != asyncThreads.end(); ++i) + { + (*i)->interrupt(); + } + + for (ThreadList::iterator i = asyncThreads.begin(); + i != asyncThreads.end(); ++i) + { + (*i)->join(); + delete *i; + } + + return; + } + // Clean up async threads. printf("Trying to clean up %d async RPC call threads...\n", asyncThreads.size()); @@ -3699,29 +3722,6 @@ void ThreadRPCServer2(void* parg) out->waitForConnection (acceptor, peer); vnThreadsRunning[4]++; - /* Note: This isn't usually called since the thread blocks - in the routine above until the program terminates. */ - if (fShutdown) - { - printf("Waiting for %d async RPC call threads to finish...\n", - asyncThreads.size()); - - for (ThreadList::iterator i = asyncThreads.begin(); - i != asyncThreads.end(); ++i) - { - (*i)->interrupt(); - } - - for (ThreadList::iterator i = asyncThreads.begin(); - i != asyncThreads.end(); ++i) - { - (*i)->join(); - delete *i; - } - - return; - } - // Restrict callers by IP if (!ClientAllowed(peer.address().to_string())) { From 1869ac1dd8a9c73848dcaaacbbef8185fbc2c040 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Wed, 26 Feb 2014 10:05:17 +0100 Subject: [PATCH 5/9] Catch exceptions correctly in async RPC calls. --- src/bitcoinrpc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 445569d3e..60cd17bb1 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -3574,6 +3574,10 @@ ExecuteRpcCall (ClientConnectionOutput* out, rpcfn_type method, string strReply = JSONRPCReply (result, json_spirit::Value::null, id); out->getStream () << HTTPReply (200, strReply) << std::flush; } + catch (Object& objError) + { + ErrorReply (out->getStream (), objError, id); + } catch (const boost::thread_interrupted& e) { ErrorReply (out->getStream (), From 52e66ea8bee1988e5e1643d8b6baebb08f4ed0c8 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Sat, 22 Mar 2014 10:36:01 +0100 Subject: [PATCH 6/9] Remove superfluous #include from merge. --- src/namecoin.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/namecoin.cpp b/src/namecoin.cpp index 3809fcfb0..4b6ff06b0 100644 --- a/src/namecoin.cpp +++ b/src/namecoin.cpp @@ -17,8 +17,6 @@ #include "json/json_spirit_utils.h" #include -#include - using namespace std; using namespace json_spirit; From 78853c9c6f082a1ac30e9f1843e9e0f41f6f2389 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Sat, 22 Mar 2014 10:47:55 +0100 Subject: [PATCH 7/9] Add stub for waitforblock async call. Add a new "waitforblock" async RPC call, that currently just sleeps for some time instead of doing actual things. --- src/bitcoinrpc.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 60cd17bb1..182af4ee0 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -3081,6 +3081,21 @@ Value getrawmempool(const Array& params, bool fHelp) return a; } +/* Block until a new block is found and return only then. */ +static Value +waitforblock (const Array& params, bool fHelp) +{ + if (fHelp || params.size () > 1) + throw runtime_error ( + "waitforblock [blockHash]\n" + "Wait for a change in the best chain (a new block being found)" + " and return the new block's hash when it arrives. If blockHash" + " is given, wait until a block with different hash is found.\n"); + + Sleep (10000); + + return "some hash"; +} // @@ -3153,6 +3168,7 @@ pair pCallTable[] = make_pair("signrawtransaction", &signrawtransaction), make_pair("sendrawtransaction", &sendrawtransaction), make_pair("getrawmempool", &getrawmempool), + make_pair("waitforblock", &waitforblock), }; map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); @@ -3190,7 +3206,11 @@ set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllo /* Methods that will be called in a new thread and can block waiting for some condition without hurting the RPC server performance. */ -set setCallAsync; +string pCallAsync[] = +{ + "waitforblock", +}; +set setCallAsync(pCallAsync, pCallAsync + sizeof(pCallAsync)/sizeof(pCallAsync[0])); From 9c8b9495c7c14b663e5447437fd3f0753bc4e5c3 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Sat, 22 Mar 2014 10:59:15 +0100 Subject: [PATCH 8/9] Implement waitforblock RPC call. Implement the body of the waitforblock RPC call, but the condition variable introduced is not yet notified when new blocks actually arrive. --- src/bitcoinrpc.cpp | 26 ++++++++++++++++++++++++-- src/main.cpp | 3 +++ src/main.h | 6 ++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 182af4ee0..1c524e8d2 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -3092,9 +3092,31 @@ waitforblock (const Array& params, bool fHelp) " and return the new block's hash when it arrives. If blockHash" " is given, wait until a block with different hash is found.\n"); - Sleep (10000); + if (IsInitialBlockDownload ()) + throw JSONRPCError (RPC_CLIENT_IN_INITIAL_DOWNLOAD, + "huntercoin is downloading blocks..."); + + uint256 lastHash; + if (params.size () > 0) + lastHash = ParseHashV (params[0], "blockHash"); + else + lastHash = hashBestChain; + + boost::unique_lock lock(mut_newBlock); + while (true) + { + /* Atomically check whether we have found a new best block and return + it if that's the case. We use a lock on cs_main in order to + prevent race conditions. */ + CRITICAL_BLOCK(cs_main) + { + if (lastHash != hashBestChain) + return hashBestChain.GetHex (); + } - return "some hash"; + /* Wait on the condition variable. */ + cv_newBlock.wait (lock); + } } diff --git a/src/main.cpp b/src/main.cpp index 1fe6ec39f..f067e8664 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,9 @@ set setpwalletRegistered; CCriticalSection cs_main; +boost::mutex mut_newBlock; +boost::condition_variable cv_newBlock; + map mapTransactions; CCriticalSection cs_mapTransactions; unsigned int nTransactionsUpdated = 0; diff --git a/src/main.h b/src/main.h index 4fc6b7682..c0610d1ce 100644 --- a/src/main.h +++ b/src/main.h @@ -12,6 +12,7 @@ #include #include +#include #ifdef __WXMSW__ #include /* for _commit */ @@ -76,6 +77,11 @@ extern int64 nTimeBestReceived; extern CCriticalSection cs_setpwalletRegistered; extern std::set setpwalletRegistered; +/* Synchronisation and condition variable for threads waiting to be notified + when a new block arrives. */ +extern boost::mutex mut_newBlock; +extern boost::condition_variable cv_newBlock; + // Settings extern int fGenerateBitcoins; extern int64 nTransactionFee; From 90f446676bcc7253f1a4f851fe2846033767cb8c Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Sat, 22 Mar 2014 15:14:27 +0100 Subject: [PATCH 9/9] Actually notify threads waiting on new blocks. When a new block has finished processing, notify all threads waiting on the condition variable about it so that waitforblock now fully works. --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index f067e8664..ddfaed0c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1388,6 +1388,10 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) nTransactionsUpdated++; printf("SetBestChain: new best=%s height=%d work=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainWork.ToString().c_str()); + /* When everything is done, notify threads waiting for a change in the + currently best chain. */ + cv_newBlock.notify_all (); + return true; }