From 2014e312b18a9bd7ea7a3899534bc1c896ea578e Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sat, 29 Mar 2008 18:47:24 +0000 Subject: [PATCH] exposed connection_candidates in torrent_status and made a small optimization to not attempt to connect peers on swarms that don't have any connect candidates --- docs/manual.rst | 7 + examples/client_test.cpp | 2 +- include/libtorrent/policy.hpp | 34 +-- include/libtorrent/torrent_handle.hpp | 4 + src/policy.cpp | 345 +++----------------------- src/torrent.cpp | 9 +- 6 files changed, 66 insertions(+), 335 deletions(-) diff --git a/docs/manual.rst b/docs/manual.rst index 6f11f54ff..ca395a9b5 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -2096,6 +2096,8 @@ It contains the following fields:: int num_complete; int num_incomplete; + int connect_candidates; + const std::vector* pieces; int num_pieces; @@ -2211,6 +2213,11 @@ not be available from all trackers. If these are not -1, they are the total number of peers that are seeding (complete) and the total number of peers that are still downloading (incomplete) this torrent. +``connect_candidates`` is the number of peers in this torrent's peer list +that is a candidate to be connected to. i.e. It has fewer connect attempts +than the max fail count, it is not a seed if we are a seed, it is not banned +etc. If this is 0, it means we don't know of any more peers that we can try. + ``total_done`` is the total number of bytes of the file(s) that we have. All this does not necessarily has to be downloaded during this session (that's ``total_payload_download``). diff --git a/examples/client_test.cpp b/examples/client_test.cpp index b9d7f1857..4a1d2f11f 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -1134,7 +1134,7 @@ int main(int ac, char* av[]) out << progress_bar(s.progress, terminal_width - 63, progress_bar_color); out << "\n"; out << " total downloaded: " << esc("32") << s.total_done << esc("0") << " Bytes "; - out << "peers: " << s.num_peers << " " + out << "peers: " << s.num_peers << " (" << s.connect_candidates << ") " << "seeds: " << s.num_seeds << " " << "distributed copies: " << s.distributed_copies << "\n" << " magnet-link: " << make_magnet_uri(h) << "\n" diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index 27d790d13..2735d14cb 100755 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -231,23 +231,20 @@ namespace libtorrent bool has_peer(policy::peer const* p) const; - private: -/* - bool unchoke_one_peer(); - void choke_one_peer(); - iterator find_choke_candidate(); - iterator find_unchoke_candidate(); + int num_connect_candidates() const { return m_num_connect_candidates; } + void recalculate_connect_candidates() + { + if (m_num_connect_candidates == 0) + m_num_connect_candidates = 1; + } + + private: - // the seed prefix means that the - // function is used while seeding. - bool seed_unchoke_one_peer(); - void seed_choke_one_peer(); - iterator find_seed_choke_candidate(); - iterator find_seed_unchoke_candidate(); -*/ iterator find_disconnect_candidate(); iterator find_connect_candidate(); + bool is_connect_candidate(peer const& p, bool finished); + std::multimap m_peers; torrent* m_torrent; @@ -256,10 +253,13 @@ namespace libtorrent // been distributed yet. size_type m_available_free_upload; - // if there is a connection limit, - // we disconnect one peer every minute in hope of - // establishing a connection with a better peer -// ptime m_last_optimistic_disconnect; + // The number of peers in our peer list + // that are connect candidates. i.e. they're + // not already connected and they have not + // yet reached their max try count and they + // have the connectable state (we have a listen + // port for them). + int m_num_connect_candidates; }; } diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index b174ec425..b191679a0 100755 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -187,6 +187,10 @@ namespace libtorrent // (including seeds), but are not necessarily // connected to int list_peers; + + // the number of peers in our peerlist that + // we potentially could connect to + int connect_candidates; const std::vector* pieces; diff --git a/src/policy.cpp b/src/policy.cpp index d5b331572..29a1d684b 100755 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -346,7 +346,7 @@ namespace libtorrent policy::policy(torrent* t) : m_torrent(t) , m_available_free_upload(0) -// , m_last_optimistic_disconnect(min_time()) + , m_num_connect_candidates(0) { TORRENT_ASSERT(t); } // disconnects and removes all peers that are now filtered @@ -388,89 +388,22 @@ namespace libtorrent m_peers.erase(i++); } } -/* - // finds the peer that has the worst download rate - // and returns it. May return 0 if all peers are - // choked. - policy::iterator policy::find_choke_candidate() + + bool policy::is_connect_candidate(peer const& p, bool finished) { - INVARIANT_CHECK; - - iterator worst_peer = m_peers.end(); - size_type min_weight = (std::numeric_limits::min)(); - -#ifndef NDEBUG - int unchoked_counter = m_num_unchoked; -#endif + if (p.connection + || p.banned + || p.type == peer::not_connectable + || (p.seed && finished) + || p.failcount >= m_torrent->settings().max_failcount) + return false; - // TODO: make this selection better - - for (iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - - if (c == 0) continue; - if (c->is_choked()) continue; -#ifndef NDEBUG - unchoked_counter--; -#endif - if (c->is_disconnecting()) continue; - // if the peer isn't interested, just choke it - if (!c->is_peer_interested()) - return i; - - size_type diff = i->total_download() - - i->total_upload(); - - size_type weight = static_cast(c->statistics().download_rate() * 10.f) - + diff - + ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024; - - if (weight >= min_weight && worst_peer != m_peers.end()) continue; - - min_weight = weight; - worst_peer = i; - continue; - } - TORRENT_ASSERT(unchoked_counter == 0); - return worst_peer; + aux::session_impl& ses = m_torrent->session(); + if (ses.m_port_filter.access(p.ip.port()) & port_filter::blocked) + return false; + return true; } - policy::iterator policy::find_unchoke_candidate() - { - INVARIANT_CHECK; - - // if all of our peers are unchoked, there's - // no left to unchoke - if (m_num_unchoked == m_torrent->num_peers()) - return m_peers.end(); - - iterator unchoke_peer = m_peers.end(); - ptime min_time = libtorrent::min_time(); - float max_down_speed = 0.f; - - // TODO: make this selection better - - for (iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - if (c == 0) continue; - if (c->is_disconnecting()) continue; - if (!c->is_choked()) continue; - if (!c->is_peer_interested()) continue; - if (c->share_diff() < -free_upload_amount - && m_torrent->ratio() != 0) continue; - if (c->statistics().download_rate() < max_down_speed) continue; - - min_time = i->last_optimistically_unchoked; - max_down_speed = c->statistics().download_rate(); - unchoke_peer = i; - } - return unchoke_peer; - } -*/ policy::iterator policy::find_disconnect_candidate() { INVARIANT_CHECK; @@ -523,7 +456,6 @@ namespace libtorrent ptime min_connect_time(now); iterator candidate = m_peers.end(); - int max_failcount = m_torrent->settings().max_failcount; int min_reconnect_time = m_torrent->settings().min_reconnect_time; int min_cidr_distance = (std::numeric_limits::max)(); bool finished = m_torrent->is_finished(); @@ -538,15 +470,11 @@ namespace libtorrent external_ip = address_v4(bytes); } - aux::session_impl& ses = m_torrent->session(); - + int connect_candidates = 0; for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) { - if (i->second.connection) continue; - if (i->second.banned) continue; - if (i->second.type == peer::not_connectable) continue; - if (i->second.seed && finished) continue; - if (i->second.failcount >= max_failcount) continue; + if (!is_connect_candidate(i->second, finished)) continue; + ++connect_candidates; // prefer peers with lower failcount if (candidate != m_peers.end() @@ -555,8 +483,6 @@ namespace libtorrent if (now - i->second.connected < seconds(i->second.failcount * min_reconnect_time)) continue; - if (ses.m_port_filter.access(i->second.ip.port()) & port_filter::blocked) - continue; TORRENT_ASSERT(i->second.connected <= now); @@ -577,6 +503,7 @@ namespace libtorrent candidate = i; } + m_num_connect_candidates = connect_candidates; TORRENT_ASSERT(min_connect_time <= now); #if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING @@ -594,113 +521,7 @@ namespace libtorrent return candidate; } -/* - policy::iterator policy::find_seed_choke_candidate() - { - INVARIANT_CHECK; - TORRENT_ASSERT(m_num_unchoked > 0); - // first choice candidate. - // it is a candidate we owe nothing to and which has been unchoked - // the longest. - iterator candidate = m_peers.end(); - - // not valid when candidate == 0 - ptime last_unchoke = min_time(); - - // second choice candidate. - // if there is no first choice candidate, this candidate will be chosen. - // it is the candidate that we owe the least to. - iterator second_candidate = m_peers.end(); - size_type lowest_share_diff = 0; // not valid when secondCandidate==0 - - for (iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - // ignore peers that are choked or - // whose connection is closed - if (c == 0) continue; - - if (c->is_choked()) continue; - if (c->is_disconnecting()) continue; - - size_type share_diff = c->share_diff(); - - // select as second candidate the one that we owe the least - // to - if (second_candidate == m_peers.end() - || share_diff <= lowest_share_diff) - { - lowest_share_diff = share_diff; - second_candidate = i; - } - - // select as first candidate the one that we don't owe anything to - // and has been waiting for an unchoke the longest - if (share_diff > 0) continue; - if (candidate == m_peers.end() - || last_unchoke > i->last_optimistically_unchoked) - { - last_unchoke = i->last_optimistically_unchoked; - candidate = i; - } - } - if (candidate != m_peers.end()) return candidate; - TORRENT_ASSERT(second_candidate != m_peers.end()); - return second_candidate; - } - - policy::iterator policy::find_seed_unchoke_candidate() - { - INVARIANT_CHECK; - - iterator candidate = m_peers.end(); - ptime last_unchoke = time_now(); - - for (iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - if (c == 0) continue; - if (!c->is_choked()) continue; - if (!c->is_peer_interested()) continue; - if (c->is_disconnecting()) continue; - if (last_unchoke < i->last_optimistically_unchoked) continue; - last_unchoke = i->last_optimistically_unchoked; - candidate = i; - } - return candidate; - } - - bool policy::seed_unchoke_one_peer() - { - INVARIANT_CHECK; - - iterator p = find_seed_unchoke_candidate(); - if (p != m_peers.end()) - { - TORRENT_ASSERT(p->connection->is_choked()); - p->connection->send_unchoke(); - p->last_optimistically_unchoked = time_now(); - ++m_num_unchoked; - } - return p != m_peers.end(); - } - - void policy::seed_choke_one_peer() - { - INVARIANT_CHECK; - - iterator p = find_seed_choke_candidate(); - if (p != m_peers.end()) - { - TORRENT_ASSERT(!p->connection->is_choked()); - p->connection->send_choke(); - --m_num_unchoked; - } - } -*/ void policy::pulse() { INVARIANT_CHECK; @@ -808,123 +629,6 @@ namespace libtorrent , m_torrent->end() , m_available_free_upload); } -/* - // ------------------------ - // seed choking policy - // ------------------------ - if (m_torrent->is_seed()) - { - if (m_num_unchoked > m_torrent->m_uploads_quota.given) - { - do - { - iterator p = find_seed_choke_candidate(); - --m_num_unchoked; - TORRENT_ASSERT(p != m_peers.end()); - if (p == m_peers.end()) break; - - TORRENT_ASSERT(!p->connection->is_choked()); - p->connection->send_choke(); - } while (m_num_unchoked > m_torrent->m_uploads_quota.given); - } - else if (m_num_unchoked > 0) - { - // optimistic unchoke. trade the 'worst' - // unchoked peer with one of the choked - // TODO: This rotation should happen - // far less frequent than this! - TORRENT_ASSERT(m_num_unchoked <= m_torrent->num_peers()); - iterator p = find_seed_unchoke_candidate(); - if (p != m_peers.end()) - { - TORRENT_ASSERT(p->connection->is_choked()); - seed_choke_one_peer(); - p->connection->send_unchoke(); - ++m_num_unchoked; - } - - } - - // make sure we have enough - // unchoked peers - while (m_num_unchoked < m_torrent->m_uploads_quota.given) - { - if (!seed_unchoke_one_peer()) break; - } -#ifndef NDEBUG - check_invariant(); -#endif - } - - // ---------------------------- - // downloading choking policy - // ---------------------------- - else - { - if (m_torrent->ratio() != 0) - { - // choke peers that have leeched too much without giving anything back - for (iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - peer_connection* c = i->connection; - if (c == 0) continue; - - size_type diff = i->connection->share_diff(); - if (diff < -free_upload_amount - && !c->is_choked()) - { - // if we have uploaded more than a piece for free, choke peer and - // wait until we catch up with our download. - c->send_choke(); - --m_num_unchoked; - } - } - } - - if (m_torrent->m_uploads_quota.given < m_torrent->num_peers()) - { - TORRENT_ASSERT(m_torrent->m_uploads_quota.given >= 0); - - // make sure we don't have too many - // unchoked peers - if (m_num_unchoked > m_torrent->m_uploads_quota.given) - { - do - { - iterator p = find_choke_candidate(); - if (p == m_peers.end()) break; - TORRENT_ASSERT(p != m_peers.end()); - TORRENT_ASSERT(!p->connection->is_choked()); - p->connection->send_choke(); - --m_num_unchoked; - } while (m_num_unchoked > m_torrent->m_uploads_quota.given); - } - // this should prevent the choke/unchoke - // problem, since it will not unchoke unless - // there actually are any choked peers - else if (count_choked() > 0) - { - // optimistic unchoke. trade the 'worst' - // unchoked peer with one of the choked - TORRENT_ASSERT(m_num_unchoked <= m_torrent->num_peers()); - iterator p = find_unchoke_candidate(); - if (p != m_peers.end()) - { - TORRENT_ASSERT(p->connection->is_choked()); - choke_one_peer(); - p->connection->send_unchoke(); - ++m_num_unchoked; - } - } - } - - // make sure we have enough - // unchoked peers - while (m_num_unchoked < m_torrent->m_uploads_quota.given - && unchoke_one_peer()); - } -*/ } int policy::count_choked() const @@ -1038,7 +742,8 @@ namespace libtorrent TORRENT_ASSERT(i->second.connection); if (!c.fast_reconnect()) i->second.connected = time_now(); -// m_last_optimistic_disconnect = time_now(); + if (m_num_connect_candidates > 0) + --m_num_connect_candidates; return true; } @@ -1167,8 +872,8 @@ namespace libtorrent // if this peer has failed before, decrease the // counter to allow it another try, since somebody // else is appearantly able to connect to it - // if it comes from the DHT it might be stale though - if (i->second.failcount > 0 && src != peer_info::dht) + // only trust this if it comes from the tracker + if (i->second.failcount > 0 && src == peer_info::tracker) --i->second.failcount; // if we're connected to this peer @@ -1191,6 +896,10 @@ namespace libtorrent } #endif } + + if (is_connect_candidate(i->second, m_torrent->is_finished())) + ++m_num_connect_candidates; + return &i->second; } catch(std::exception& e) @@ -1442,6 +1151,9 @@ namespace libtorrent // p->connected = time_now(); } + if (is_connect_candidate(*p, m_torrent->is_finished())) + ++m_num_connect_candidates; + // if the share ratio is 0 (infinite), the // m_available_free_upload isn't used, // because it isn't necessary. @@ -1484,6 +1196,7 @@ namespace libtorrent void policy::check_invariant() const { + TORRENT_ASSERT(m_num_connect_candidates >= 0); if (m_torrent->is_aborted()) return; int connected_peers = 0; diff --git a/src/torrent.cpp b/src/torrent.cpp index 05584a41c..c47762c64 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1618,6 +1618,11 @@ namespace libtorrent { for (peer_iterator i = begin(); i != end(); ++i) (*i)->update_interest(); + + // if we used to be finished, but we aren't anymore + // we may need to connect to peers again + if (!is_finished()) + m_policy.recalculate_connect_candidates(); } void torrent::filter_piece(int index, bool filter) @@ -2471,7 +2476,8 @@ namespace libtorrent return int(m_connections.size()) < m_max_connections && !m_paused && m_state != torrent_status::checking_files - && m_state != torrent_status::queued_for_checking; + && m_state != torrent_status::queued_for_checking + && m_policy.num_connect_candidates() > 0; } void torrent::disconnect_all() @@ -3361,6 +3367,7 @@ namespace libtorrent st.list_peers = std::distance(m_policy.begin_peer(), m_policy.end_peer()); st.list_seeds = (int)std::count_if(m_policy.begin_peer(), m_policy.end_peer() , boost::bind(&policy::peer::seed, bind(&policy::iterator::value_type::second, _1))); + st.connect_candidates = m_policy.num_connect_candidates(); st.storage_mode = m_storage_mode;