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:
parent
2bd49c4b56
commit
2014e312b1
|
@ -2096,6 +2096,8 @@ It contains the following fields::
|
|||
int num_complete;
|
||||
int num_incomplete;
|
||||
|
||||
int connect_candidates;
|
||||
|
||||
const std::vector<bool>* 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``).
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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<address, peer> 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -188,6 +188,10 @@ namespace libtorrent
|
|||
// connected to
|
||||
int list_peers;
|
||||
|
||||
// the number of peers in our peerlist that
|
||||
// we potentially could connect to
|
||||
int connect_candidates;
|
||||
|
||||
const std::vector<bool>* pieces;
|
||||
|
||||
// this is the number of pieces the client has
|
||||
|
|
345
src/policy.cpp
345
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;
|
||||
if (p.connection
|
||||
|| p.banned
|
||||
|| p.type == peer::not_connectable
|
||||
|| (p.seed && finished)
|
||||
|| p.failcount >= m_torrent->settings().max_failcount)
|
||||
return false;
|
||||
|
||||
iterator worst_peer = m_peers.end();
|
||||
size_type min_weight = (std::numeric_limits<int>::min)();
|
||||
|
||||
#ifndef NDEBUG
|
||||
int unchoked_counter = m_num_unchoked;
|
||||
#endif
|
||||
|
||||
// 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<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;
|
||||
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<int>::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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue