epee: fix hang on exit

When the boost ioservice is stopped, pending work notifications
will not happen. This includes deadline timers, which would
otherwise time out the now cancelled I/O operations. When this
happens just after starting a new connect operation, this can
leave that operations in a state where it won't receive either
the completion notification nor a timeout, causing a hang.

This is fixed by keeping a list of connections corresponding
to the connect operations, and cancelling them before stopping
the boost ioservice.

Note that the list of these connections can grow unbounded, as
they're never cleaned up. Cleaning them up would involve
working out which connections do not have any pending work,
and it's not quite clear yet how to go about this.
This commit is contained in:
moneromooo-monero 2015-12-22 12:31:22 +00:00
parent 17ff6f2114
commit 1e2f2d7da0
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
2 changed files with 25 additions and 1 deletions

View file

@ -111,6 +111,8 @@ namespace net_utils
bool speed_limit_is_enabled() const; ///< tells us should we be sleeping here (e.g. do not sleep on RPC connections) bool speed_limit_is_enabled() const; ///< tells us should we be sleeping here (e.g. do not sleep on RPC connections)
bool cancel();
private: private:
//----------------- i_service_endpoint --------------------- //----------------- i_service_endpoint ---------------------
virtual bool do_send(const void* ptr, size_t cb); ///< (see do_send from i_service_endpoint) virtual bool do_send(const void* ptr, size_t cb); ///< (see do_send from i_service_endpoint)
@ -303,6 +305,9 @@ namespace net_utils
/// The next connection to be accepted /// The next connection to be accepted
connection_ptr new_connection_; connection_ptr new_connection_;
std::mutex connections_mutex;
std::deque<connection_ptr> connections_;
}; // class <>boosted_tcp_server }; // class <>boosted_tcp_server

View file

@ -565,7 +565,12 @@ PRAGMA_WARNING_DISABLE_VS(4355)
return true; return true;
CATCH_ENTRY_L0("connection<t_protocol_handler>::close", false); CATCH_ENTRY_L0("connection<t_protocol_handler>::close", false);
} }
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
bool connection<t_protocol_handler>::cancel()
{
return close();
}
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
template<class t_protocol_handler> template<class t_protocol_handler>
void connection<t_protocol_handler>::handle_write(const boost::system::error_code& e, size_t cb) void connection<t_protocol_handler>::handle_write(const boost::system::error_code& e, size_t cb)
@ -871,6 +876,12 @@ POP_WARNINGS
} }
m_stop_signal_sent = true; m_stop_signal_sent = true;
TRY_ENTRY(); TRY_ENTRY();
connections_mutex.lock();
for (auto &c: connections_)
{
c->cancel();
}
connections_mutex.unlock();
io_service_.stop(); io_service_.stop();
CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void()); CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void());
} }
@ -914,6 +925,10 @@ POP_WARNINGS
TRY_ENTRY(); TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) ); connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) );
connections_mutex.lock();
connections_.push_back(new_connection_l);
LOG_PRINT_L2("connections_ size now " << connections_.size());
connections_mutex.unlock();
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -1006,6 +1021,10 @@ POP_WARNINGS
{ {
TRY_ENTRY(); TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) ); connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) );
connections_mutex.lock();
connections_.push_back(new_connection_l);
LOG_PRINT_L2("connections_ size now " << connections_.size());
connections_mutex.unlock();
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////