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

This commit is contained in:
Arvid Norberg 2008-03-29 18:47:24 +00:00
parent 2bd49c4b56
commit 2014e312b1
6 changed files with 66 additions and 335 deletions

View File

@ -2096,6 +2096,8 @@ It contains the following fields::
int num_complete; int num_complete;
int num_incomplete; int num_incomplete;
int connect_candidates;
const std::vector<bool>* pieces; const std::vector<bool>* pieces;
int num_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 number of peers that are seeding (complete) and the total number of peers
that are still downloading (incomplete) this torrent. 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 ``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 this does not necessarily has to be downloaded during this session (that's
``total_payload_download``). ``total_payload_download``).

View File

@ -1134,7 +1134,7 @@ int main(int ac, char* av[])
out << progress_bar(s.progress, terminal_width - 63, progress_bar_color); out << progress_bar(s.progress, terminal_width - 63, progress_bar_color);
out << "\n"; out << "\n";
out << " total downloaded: " << esc("32") << s.total_done << esc("0") << " Bytes "; 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 << " " << "seeds: " << s.num_seeds << " "
<< "distributed copies: " << s.distributed_copies << "\n" << "distributed copies: " << s.distributed_copies << "\n"
<< " magnet-link: " << make_magnet_uri(h) << "\n" << " magnet-link: " << make_magnet_uri(h) << "\n"

View File

@ -231,23 +231,20 @@ namespace libtorrent
bool has_peer(policy::peer const* p) const; bool has_peer(policy::peer const* p) const;
private: int num_connect_candidates() const { return m_num_connect_candidates; }
/* void recalculate_connect_candidates()
bool unchoke_one_peer(); {
void choke_one_peer(); if (m_num_connect_candidates == 0)
iterator find_choke_candidate(); m_num_connect_candidates = 1;
iterator find_unchoke_candidate(); }
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_disconnect_candidate();
iterator find_connect_candidate(); iterator find_connect_candidate();
bool is_connect_candidate(peer const& p, bool finished);
std::multimap<address, peer> m_peers; std::multimap<address, peer> m_peers;
torrent* m_torrent; torrent* m_torrent;
@ -256,10 +253,13 @@ namespace libtorrent
// been distributed yet. // been distributed yet.
size_type m_available_free_upload; size_type m_available_free_upload;
// if there is a connection limit, // The number of peers in our peer list
// we disconnect one peer every minute in hope of // that are connect candidates. i.e. they're
// establishing a connection with a better peer // not already connected and they have not
// ptime m_last_optimistic_disconnect; // yet reached their max try count and they
// have the connectable state (we have a listen
// port for them).
int m_num_connect_candidates;
}; };
} }

View File

@ -187,6 +187,10 @@ namespace libtorrent
// (including seeds), but are not necessarily // (including seeds), but are not necessarily
// connected to // connected to
int list_peers; int list_peers;
// the number of peers in our peerlist that
// we potentially could connect to
int connect_candidates;
const std::vector<bool>* pieces; const std::vector<bool>* pieces;

View File

@ -346,7 +346,7 @@ namespace libtorrent
policy::policy(torrent* t) policy::policy(torrent* t)
: m_torrent(t) : m_torrent(t)
, m_available_free_upload(0) , m_available_free_upload(0)
// , m_last_optimistic_disconnect(min_time()) , m_num_connect_candidates(0)
{ TORRENT_ASSERT(t); } { TORRENT_ASSERT(t); }
// disconnects and removes all peers that are now filtered // disconnects and removes all peers that are now filtered
@ -388,89 +388,22 @@ namespace libtorrent
m_peers.erase(i++); m_peers.erase(i++);
} }
} }
/*
// finds the peer that has the worst download rate bool policy::is_connect_candidate(peer const& p, bool finished)
// and returns it. May return 0 if all peers are
// choked.
policy::iterator policy::find_choke_candidate()
{ {
INVARIANT_CHECK; if (p.connection
|| p.banned
iterator worst_peer = m_peers.end(); || p.type == peer::not_connectable
size_type min_weight = (std::numeric_limits<int>::min)(); || (p.seed && finished)
|| p.failcount >= m_torrent->settings().max_failcount)
#ifndef NDEBUG return false;
int unchoked_counter = m_num_unchoked;
#endif
// TODO: make this selection better aux::session_impl& ses = m_torrent->session();
if (ses.m_port_filter.access(p.ip.port()) & port_filter::blocked)
for (iterator i = m_peers.begin(); return false;
i != m_peers.end(); ++i) return true;
{
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<int>(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;
} }
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() policy::iterator policy::find_disconnect_candidate()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@ -523,7 +456,6 @@ namespace libtorrent
ptime min_connect_time(now); ptime min_connect_time(now);
iterator candidate = m_peers.end(); 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_reconnect_time = m_torrent->settings().min_reconnect_time;
int min_cidr_distance = (std::numeric_limits<int>::max)(); int min_cidr_distance = (std::numeric_limits<int>::max)();
bool finished = m_torrent->is_finished(); bool finished = m_torrent->is_finished();
@ -538,15 +470,11 @@ namespace libtorrent
external_ip = address_v4(bytes); 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) for (iterator i = m_peers.begin(); i != m_peers.end(); ++i)
{ {
if (i->second.connection) continue; if (!is_connect_candidate(i->second, finished)) continue;
if (i->second.banned) continue; ++connect_candidates;
if (i->second.type == peer::not_connectable) continue;
if (i->second.seed && finished) continue;
if (i->second.failcount >= max_failcount) continue;
// prefer peers with lower failcount // prefer peers with lower failcount
if (candidate != m_peers.end() if (candidate != m_peers.end()
@ -555,8 +483,6 @@ namespace libtorrent
if (now - i->second.connected < seconds(i->second.failcount * min_reconnect_time)) if (now - i->second.connected < seconds(i->second.failcount * min_reconnect_time))
continue; continue;
if (ses.m_port_filter.access(i->second.ip.port()) & port_filter::blocked)
continue;
TORRENT_ASSERT(i->second.connected <= now); TORRENT_ASSERT(i->second.connected <= now);
@ -577,6 +503,7 @@ namespace libtorrent
candidate = i; candidate = i;
} }
m_num_connect_candidates = connect_candidates;
TORRENT_ASSERT(min_connect_time <= now); TORRENT_ASSERT(min_connect_time <= now);
#if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING #if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING
@ -594,113 +521,7 @@ namespace libtorrent
return candidate; 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() void policy::pulse()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@ -808,123 +629,6 @@ namespace libtorrent
, m_torrent->end() , m_torrent->end()
, m_available_free_upload); , 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 int policy::count_choked() const
@ -1038,7 +742,8 @@ namespace libtorrent
TORRENT_ASSERT(i->second.connection); TORRENT_ASSERT(i->second.connection);
if (!c.fast_reconnect()) if (!c.fast_reconnect())
i->second.connected = time_now(); i->second.connected = time_now();
// m_last_optimistic_disconnect = time_now(); if (m_num_connect_candidates > 0)
--m_num_connect_candidates;
return true; return true;
} }
@ -1167,8 +872,8 @@ namespace libtorrent
// if this peer has failed before, decrease the // if this peer has failed before, decrease the
// counter to allow it another try, since somebody // counter to allow it another try, since somebody
// else is appearantly able to connect to it // else is appearantly able to connect to it
// if it comes from the DHT it might be stale though // only trust this if it comes from the tracker
if (i->second.failcount > 0 && src != peer_info::dht) if (i->second.failcount > 0 && src == peer_info::tracker)
--i->second.failcount; --i->second.failcount;
// if we're connected to this peer // if we're connected to this peer
@ -1191,6 +896,10 @@ namespace libtorrent
} }
#endif #endif
} }
if (is_connect_candidate(i->second, m_torrent->is_finished()))
++m_num_connect_candidates;
return &i->second; return &i->second;
} }
catch(std::exception& e) catch(std::exception& e)
@ -1442,6 +1151,9 @@ namespace libtorrent
// p->connected = time_now(); // 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 // if the share ratio is 0 (infinite), the
// m_available_free_upload isn't used, // m_available_free_upload isn't used,
// because it isn't necessary. // because it isn't necessary.
@ -1484,6 +1196,7 @@ namespace libtorrent
void policy::check_invariant() const void policy::check_invariant() const
{ {
TORRENT_ASSERT(m_num_connect_candidates >= 0);
if (m_torrent->is_aborted()) return; if (m_torrent->is_aborted()) return;
int connected_peers = 0; int connected_peers = 0;

View File

@ -1618,6 +1618,11 @@ namespace libtorrent
{ {
for (peer_iterator i = begin(); i != end(); ++i) for (peer_iterator i = begin(); i != end(); ++i)
(*i)->update_interest(); (*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) void torrent::filter_piece(int index, bool filter)
@ -2471,7 +2476,8 @@ namespace libtorrent
return int(m_connections.size()) < m_max_connections return int(m_connections.size()) < m_max_connections
&& !m_paused && !m_paused
&& m_state != torrent_status::checking_files && 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() 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_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() 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))); , 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; st.storage_mode = m_storage_mode;