From 136f101449bcd754a15701896faefecc7a054e17 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Mon, 11 May 2009 20:23:47 +0000 Subject: [PATCH] when reaching peer list size limit, rank peers to be removed. Fix for updating IP filter --- include/libtorrent/policy.hpp | 21 +++- src/policy.cpp | 194 ++++++++++++++++++++++++++++------ src/session_impl.cpp | 2 + 3 files changed, 179 insertions(+), 38 deletions(-) diff --git a/include/libtorrent/policy.hpp b/include/libtorrent/policy.hpp index 23fdf56ec..fa37ff6fa 100644 --- a/include/libtorrent/policy.hpp +++ b/include/libtorrent/policy.hpp @@ -282,7 +282,7 @@ namespace libtorrent struct peer_ptr_compare { - bool operator()(peer* lhs, peer* rhs) const + bool operator()(peer const* lhs, peer const* rhs) const { return lhs->address() < rhs->address(); } }; @@ -294,12 +294,19 @@ namespace libtorrent iterator end_peer() { return m_peers.end(); } const_iterator begin_peer() const { return m_peers.begin(); } const_iterator end_peer() const { return m_peers.end(); } + std::pair find_peers(address const& a) { peer tmp(a); peer_ptr_compare cmp; - return std::equal_range(m_peers.begin(), m_peers.end() - , &tmp, cmp); + return std::equal_range(m_peers.begin(), m_peers.end(), &tmp, cmp); + } + + std::pair find_peers(address const& a) const + { + peer tmp(a); + peer_ptr_compare cmp; + return std::equal_range(m_peers.begin(), m_peers.end(), &tmp, cmp); } bool connect_one_peer(int session_time); @@ -310,16 +317,22 @@ namespace libtorrent int num_connect_candidates() const { return m_num_connect_candidates; } void recalculate_connect_candidates(); + void erase_peer(policy::peer* p); void erase_peer(iterator i); private: + bool compare_peer_erase(policy::peer const& lhs, policy::peer const& rhs) const; bool compare_peer(policy::peer const& lhs, policy::peer const& rhs , address const& external_ip) const; iterator find_connect_candidate(int session_time); - bool is_connect_candidate(peer const& p, bool finished); + bool is_connect_candidate(peer const& p, bool finished) const; + bool is_erase_candidate(peer const& p, bool finished) const; + bool should_erase_immediately(peer const& p) const; + + void erase_peers(); peers_t m_peers; diff --git a/src/policy.cpp b/src/policy.cpp index e57440c95..5fea8160b 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -357,8 +357,8 @@ namespace libtorrent { aux::session_impl& ses = m_torrent->session(); piece_picker* p = 0; - if (m_torrent->has_picker()) - p = &m_torrent->picker(); + if (m_torrent->has_picker()) p = &m_torrent->picker(); + for (iterator i = m_peers.begin(); i != m_peers.end();) { if ((ses.m_ip_filter.access((*i)->address()) & ip_filter::blocked) == 0) @@ -380,10 +380,22 @@ namespace libtorrent if (ses.m_alerts.should_post()) ses.m_alerts.post_alert(peer_blocked_alert((*i)->address())); } - erase_peer(i++); + int current = i - m_peers.begin(); + erase_peer(i); + i = m_peers.begin() + current; } } + void policy::erase_peer(policy::peer* p) + { + INVARIANT_CHECK; + + std::pair range = find_peers(p->address()); + iterator iter = std::find_if(range.first, range.second, match_peer_endpoint(p->ip())); + if (iter == range.second) return; + erase_peer(iter); + } + // any peer that is erased from m_peers will be // erased through this function. This way we can make // sure that any references to the peer are removed @@ -405,7 +417,72 @@ namespace libtorrent m_peers.erase(i); } - bool policy::is_connect_candidate(peer const& p, bool finished) + bool policy::should_erase_immediately(peer const& p) const + { + return p.source == peer_info::resume_data + && p.failcount > 0 + && !p.banned; + } + + bool policy::is_erase_candidate(peer const& pe, bool finished) const + { + return pe.connection == 0 + && pe.last_connected != 0 + && !pe.banned + && !is_connect_candidate(pe, m_finished) + && m_peers.size() >= m_torrent->settings().max_peerlist_size * 0.95 + && m_torrent->settings().max_peerlist_size > 0; + } + + void policy::erase_peers() + { + INVARIANT_CHECK; + + if (m_torrent->settings().max_peerlist_size == 0 + || m_peers.empty()) return; + + int erase_candidate = -1; + + TORRENT_ASSERT(m_finished == m_torrent->is_finished()); + + int round_robin = rand() % m_peers.size(); + + for (int iterations = (std::min)(int(m_peers.size()), 300); + iterations > 0; --iterations) + { + if (m_peers.size() < m_torrent->settings().max_peerlist_size * 0.95) + break; + + if (round_robin == m_peers.size()) round_robin = 0; + + peer& pe = *m_peers[round_robin]; + int current = round_robin; + + { + if (is_erase_candidate(pe, m_finished) + && (erase_candidate == -1 + || !compare_peer_erase(*m_peers[erase_candidate], pe))) + { + if (should_erase_immediately(pe)) + { + if (erase_candidate > current) --erase_candidate; + erase_peer(m_peers.begin() + current); + } + else + { + erase_candidate = current; + } + } + } + + ++round_robin; + } + + if (erase_candidate > -1) + erase_peer(m_peers.begin() + erase_candidate); + } + + bool policy::is_connect_candidate(peer const& p, bool finished) const { if (p.connection || p.banned @@ -414,7 +491,7 @@ namespace libtorrent || p.failcount >= m_torrent->settings().max_failcount) return false; - aux::session_impl& ses = m_torrent->session(); + aux::session_impl const& ses = m_torrent->session(); if (ses.m_port_filter.access(p.port) & port_filter::blocked) return false; return true; @@ -425,6 +502,7 @@ namespace libtorrent INVARIANT_CHECK; int candidate = -1; + int erase_candidate = -1; TORRENT_ASSERT(m_finished == m_torrent->is_finished()); @@ -470,22 +548,25 @@ namespace libtorrent #endif // if the number of peers is growing large // we need to start weeding. - // don't remove peers we're connected to - // don't remove peers we've never even tried - // don't remove banned peers unless they're 2 - // hours old. They should remain banned for - // at least that long - // don't remove peers that we still can try again - if (pe.connection == 0 - && pe.last_connected != 0 - && (!pe.banned || session_time - pe.last_connected > 2 * 60 * 60) - && !is_connect_candidate(pe, m_finished) - && m_peers.size() >= m_torrent->settings().max_peerlist_size * 0.9 + + if (m_peers.size() >= m_torrent->settings().max_peerlist_size * 0.95 && m_torrent->settings().max_peerlist_size > 0) { - if (candidate > m_round_robin) --candidate; - erase_peer(m_peers.begin() + m_round_robin); - continue; + if (is_erase_candidate(pe, m_finished) + && (erase_candidate == -1 + || !compare_peer_erase(*m_peers[erase_candidate], pe))) + { + if (should_erase_immediately(pe)) + { + if (erase_candidate > current) --erase_candidate; + if (candidate > current) --candidate; + erase_peer(m_peers.begin() + current); + } + else + { + erase_candidate = current; + } + } } ++m_round_robin; @@ -507,6 +588,12 @@ namespace libtorrent candidate = current; } + if (erase_candidate > -1) + { + if (candidate > erase_candidate) --candidate; + erase_peer(m_peers.begin() + erase_candidate); + } + #if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING if (candidate != -1) { @@ -558,6 +645,8 @@ namespace libtorrent , m_torrent->end() , m_available_free_upload); } + + erase_peers(); } bool policy::new_connection(peer_connection& c, int session_time) @@ -691,6 +780,7 @@ namespace libtorrent if (m_round_robin > iter - m_peers.begin()) ++m_round_robin; peer* p = m_torrent->session().m_peer_pool.malloc(); + if (p == 0) return false; m_torrent->session().m_peer_pool.set_next_size(500); new (p) peer(c.remote(), false, 0); iter = m_peers.insert(iter, p); @@ -792,6 +882,16 @@ namespace libtorrent return 0; } + // if the IP is blocked, don't add it + if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked) + { + if (ses.m_alerts.should_post()) + { + ses.m_alerts.post_alert(peer_blocked_alert(remote.address())); + } + return 0; + } + iterator iter; peer* i = 0; @@ -813,25 +913,29 @@ namespace libtorrent if (!found) { - // if the IP is blocked, don't add it - if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked) - { - if (ses.m_alerts.should_post()) - { - ses.m_alerts.post_alert(peer_blocked_alert(remote.address())); - } - return 0; - } - if (m_torrent->settings().max_peerlist_size && int(m_peers.size()) >= m_torrent->settings().max_peerlist_size) - return 0; + { + if (src == peer_info::resume_data) return 0; + + erase_peers(); + if (int(m_peers.size()) >= m_torrent->settings().max_peerlist_size) + return 0; + + // since some peers were removed, we need to + // update the iterator to make it valid again + peer tmp(remote.address()); + peer_ptr_compare cmp; + iter = std::lower_bound(m_peers.begin(), m_peers.end() + , &tmp, cmp); + } if (m_round_robin > iter - m_peers.begin()) ++m_round_robin; // we don't have any info about this peer. // add a new entry peer* p = m_torrent->session().m_peer_pool.malloc(); + if (p == 0) return 0; m_torrent->session().m_peer_pool.set_next_size(500); new (p) peer(remote, true, src); iter = m_peers.insert(iter, p); @@ -1108,6 +1212,17 @@ namespace libtorrent TORRENT_ASSERT(p->prev_amount_download == 0); p->prev_amount_download += c.statistics().total_payload_download(); p->prev_amount_upload += c.statistics().total_payload_upload(); + + // if we're already a seed, it's not as important + // to keep all the possibly stale peers + // if we're not a seed, but we have too many peers + // start weeding the ones we only know from resume + // data first + if (m_torrent->is_seed() || m_peers.size() >= m_torrent->settings().max_peerlist_size * 0.9) + { + if (p->source == peer_info::resume_data) + erase_peer(p); + } } void policy::peer_is_interesting(peer_connection& c) @@ -1184,10 +1299,7 @@ namespace libtorrent #endif if (!m_torrent->settings().allow_multiple_connections_per_ip) { - peer tmp(p.address()); - peer_ptr_compare cmp; - std::pair range = std::equal_range( - m_peers.begin(), m_peers.end(), &tmp, cmp); + std::pair range = find_peers(p.address()); TORRENT_ASSERT(range.second - range.first == 1); } else @@ -1356,6 +1468,20 @@ namespace libtorrent } } + // this returns true if lhs is a better erase candidate than rhs + bool policy::compare_peer_erase(policy::peer const& lhs, policy::peer const& rhs) const + { + bool lhs_resume_data_source = lhs.source == peer_info::resume_data; + bool rhs_resume_data_source = rhs.source == peer_info::resume_data; + + // prefer to drop peers whose only source is resume data + if (lhs_resume_data_source != rhs_resume_data_source) + return lhs_resume_data_source > rhs_resume_data_source; + + // prefer peers with higher failcount + return lhs.failcount > rhs.failcount; + } + // this returns true if lhs is a better connect candidate than rhs bool policy::compare_peer(policy::peer const& lhs, policy::peer const& rhs , address const& external_ip) const diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 88a5d48cc..00bdbbdd8 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1544,6 +1544,8 @@ namespace aux { } } } + +// m_peer_pool.release_memory(); } namespace