diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2014-05-09 10:01:50 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2014-05-12 09:31:06 +0200 |
commit | cef44941e798c33f7334bf90a2dd531f9bada8c3 (patch) | |
tree | 56c6bed50357f680454a968434db6f12ca38340c /src | |
parent | 381b25dfde013efae4e0360f82cd34e27662e743 (diff) |
rpc: keep track of acceptors, and cancel them in StopRPCThreads
Fixes #4156.
The problem is that the boost::asio::io_service destructor
waits for the acceptors to finish (on windows, and boost 1.55).
Fix this by keeping track of the acceptors and cancelling them before
stopping the event loops.
Diffstat (limited to 'src')
-rw-r--r-- | src/rpcserver.cpp | 17 |
1 files changed, 14 insertions, 3 deletions
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 442bf5c9cb..d4a229b092 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -39,6 +39,7 @@ static ssl::context* rpc_ssl_context = NULL; static boost::thread_group* rpc_worker_group = NULL; static boost::asio::io_service::work *rpc_dummy_work = NULL; static std::vector<CSubNet> rpc_allow_subnets; //!< List of subnets to allow RPC connections from +static std::vector< boost::shared_ptr<ip::tcp::acceptor> > rpc_acceptors; void RPCTypeCheck(const Array& params, const list<Value_type>& typesExpected, @@ -593,12 +594,13 @@ void StartRPCThreads() asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", Params().RPCPort())); boost::system::error_code v6_only_error; - boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service)); bool fListening = false; std::string strerr; try { + boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service)); + rpc_acceptors.push_back(acceptor); acceptor->open(endpoint.protocol()); acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); @@ -616,7 +618,6 @@ void StartRPCThreads() { strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); } - try { // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately if (!fListening || loopback || v6_only_error) @@ -624,7 +625,8 @@ void StartRPCThreads() bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); endpoint.address(bindAddress); - acceptor.reset(new ip::tcp::acceptor(*rpc_io_service)); + boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service)); + rpc_acceptors.push_back(acceptor); acceptor->open(endpoint.protocol()); acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor->bind(endpoint); @@ -668,7 +670,16 @@ void StopRPCThreads() { if (rpc_io_service == NULL) return; + // First, cancel all timers and acceptors + // This is not done automatically by ->stop(), and in some cases the destructor of + // asio::io_service can hang if this is skipped. + BOOST_FOREACH(const boost::shared_ptr<ip::tcp::acceptor> &acceptor, rpc_acceptors) + acceptor->cancel(); + rpc_acceptors.clear(); + BOOST_FOREACH(const PAIRTYPE(std::string, boost::shared_ptr<deadline_timer>) &timer, deadlineTimers) + timer.second->cancel(); deadlineTimers.clear(); + rpc_io_service->stop(); if (rpc_worker_group != NULL) rpc_worker_group->join_all(); |