diff --git a/include/libtorrent/connection_queue.hpp b/include/libtorrent/connection_queue.hpp index ee8875668..c67dba0c1 100644 --- a/include/libtorrent/connection_queue.hpp +++ b/include/libtorrent/connection_queue.hpp @@ -67,6 +67,22 @@ public: int limit() const; void close(); int size() const { return m_queue.size(); } + int num_connecting() const { return m_num_connecting; } +#if defined TORRENT_ASIO_DEBUGGING + float next_timeout() const { return total_milliseconds(m_timer.expires_at() - time_now_hires()) / 1000.f; } + float max_timeout() const + { + ptime max_timeout = min_time(); + for (std::list::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + if (!i->connecting) continue; + if (i->expires > max_timeout) max_timeout = i->expires; + } + if (max_timeout == min_time()) return 0.f; + return total_milliseconds(max_timeout - time_now_hires()) / 1000.f; + } +#endif #ifdef TORRENT_DEBUG void check_invariant() const; @@ -85,6 +101,9 @@ private: entry(): connecting(false), ticket(0), expires(max_time()), priority(0) {} // called when the connection is initiated // this is when the timeout countdown starts + // TODO: if we don't actually need the connection queue + // to hold ownership of objects, replace these boost functions + // with pointer to a pure virtual interface class boost::function on_connect; // called if done hasn't been called within the timeout // or if the connection queue aborts. This means there @@ -100,6 +119,9 @@ private: int priority; }; + // TODO: split this into a queue and connecting map. The key for the map + // is the ticket. Most field in entry would only be necessary for the + // connecting map. std::list m_queue; // the next ticket id a connection will be given @@ -108,6 +130,9 @@ private: int m_half_open_limit; bool m_abort; + // the number of outstanding timers + int m_num_timers; + deadline_timer m_timer; mutable mutex_t m_mutex; diff --git a/src/connection_queue.cpp b/src/connection_queue.cpp index 7b2089632..ce040e253 100644 --- a/src/connection_queue.cpp +++ b/src/connection_queue.cpp @@ -49,6 +49,7 @@ namespace libtorrent , m_num_connecting(0) , m_half_open_limit(0) , m_abort(false) + , m_num_timers(0) , m_timer(ios) #ifdef TORRENT_DEBUG , m_in_timeout_function(false) @@ -155,7 +156,7 @@ namespace libtorrent continue; } TORRENT_TRY { - e.on_timeout(); + e.on_connect(-1); } TORRENT_CATCH(std::exception&) {} tmp.pop_front(); } @@ -227,6 +228,7 @@ namespace libtorrent error_code ec; m_timer.expires_at(expire, ec); m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + ++m_num_timers; } i->connecting = true; ++m_num_connecting; @@ -275,6 +277,7 @@ namespace libtorrent complete_async("connection_queue::on_timeout"); #endif mutex_t::scoped_lock l(m_mutex); + --m_num_timers; INVARIANT_CHECK; #ifdef TORRENT_DEBUG @@ -282,7 +285,7 @@ namespace libtorrent #endif TORRENT_ASSERT(!e || e == error::operation_aborted); - if (e) return; + if (e && m_num_connecting == 0 && m_num_timers > 0) return; ptime next_expire = max_time(); ptime now = time_now_hires() + milliseconds(100); @@ -327,6 +330,7 @@ namespace libtorrent error_code ec; m_timer.expires_at(next_expire, ec); m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + ++m_num_timers; } try_connect(l); } diff --git a/src/http_connection.cpp b/src/http_connection.cpp index 1d89d1535..24bfc95d1 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -93,6 +93,7 @@ http_connection::http_connection(io_service& ios, connection_queue& cc http_connection::~http_connection() { + TORRENT_ASSERT(m_connection_ticket == -1); #ifdef TORRENT_USE_OPENSSL if (m_own_ssl_context) delete m_ssl_ctx; #endif @@ -378,23 +379,14 @@ void http_connection::start(std::string const& hostname, std::string const& port void http_connection::on_connect_timeout() { - if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); - m_connection_ticket = -1; + TORRENT_ASSERT(m_connection_ticket > -1); // keep ourselves alive even if the callback function // deletes this object boost::shared_ptr me(shared_from_this()); - if (!m_endpoints.empty()) - { - error_code ec; - m_sock.close(ec); - } - else - { - callback(asio::error::timed_out); - close(); - } + error_code ec; + m_sock.close(ec); } void http_connection::on_timeout(boost::weak_ptr p @@ -550,6 +542,12 @@ void http_connection::queue_connect() void http_connection::connect(int ticket, tcp::endpoint target_address) { + if (ticket == -1) + { + close(); + return; + } + m_connection_ticket = ticket; if (m_proxy.proxy_hostnames && (m_proxy.type == proxy_settings::socks5 diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 9de8077f0..c1fafc330 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -5245,6 +5245,12 @@ namespace libtorrent (*m_ses.m_logger) << time_now_string() << " ON_CONNECT: " << print_endpoint(m_remote) << "\n"; #endif + if (ticket == -1) + { + disconnect(asio::error::operation_aborted); + return; + } + m_connection_ticket = ticket; boost::shared_ptr t = m_torrent.lock(); diff --git a/src/session_impl.cpp b/src/session_impl.cpp index ac9ccc5d8..d0649336a 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -5495,7 +5495,9 @@ namespace aux { { sleep(1000); ++counter; - printf("\n==== Waiting to shut down: %d ==== conn-queue: %d\n\n", counter, m_half_open.size()); + printf("\n==== Waiting to shut down: %d ==== conn-queue: %d connecting: %d timeout (next: %f max: %f)\n\n" + , counter, m_half_open.size(), m_half_open.num_connecting(), m_half_open.next_timeout() + , m_half_open.max_timeout()); } async_dec_threads(); #endif diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp index 06fb58276..8bbc7341e 100644 --- a/src/udp_socket.cpp +++ b/src/udp_socket.cpp @@ -776,6 +776,11 @@ void udp_socket::on_connect(int ticket) if (m_abort) return; if (is_closed()) return; + if (ticket == -1) + { + close(); + return; + } #if defined TORRENT_ASIO_DEBUGGING add_outstanding_async("udp_socket::on_connected");