From 40804a829b0434a380472106f18c4f596b1d2586 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 15 Apr 2007 02:14:02 +0000 Subject: [PATCH] failcount is reset on a peer that successfully connects. documented max_failcount and min_reconnect_time. implemented seed optimization for piece_picker (inc_refcount_all and dec_refcount_all), ticket #17 --- docs/manual.html | 8 ++ docs/manual.rst | 10 ++ include/libtorrent/piece_picker.hpp | 6 ++ include/libtorrent/torrent.hpp | 15 +++ src/bt_peer_connection.cpp | 2 + src/peer_connection.cpp | 114 +++++++++++++++-------- src/piece_picker.cpp | 137 +++++++++++++++++++++++++++- src/torrent.cpp | 25 ++++- 8 files changed, 268 insertions(+), 49 deletions(-) diff --git a/docs/manual.html b/docs/manual.html index f72827256..e7a1a1726 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -2127,6 +2127,8 @@ struct session_settings int urlseed_pipeline_size; int file_pool_size; bool allow_multiple_connections_per_ip; + int max_failcount; + int min_reconnect_time; bool use_dht_as_fallback; }; @@ -2202,6 +2204,12 @@ connections from the same IP address is not allowed by default, to prevent abusive behavior by peers. It may be useful to allow such connections in cases where simulations are run on the same machie, and all peers in a swarm has the same IP address.

+

max_failcount is the maximum times we try to connect to a peer before +stop connecting again. If a peer succeeds, the failcounter is reset. If +a peer is retrieved from a peer source (other than DHT) the failcount is +decremented by one, allowing another try.

+

min_reconnect_time is the time to wait between connection attempts. If +the peer fails, the time is multiplied by fail counter.

use_dht_as_fallback determines how the DHT is used. If this is true (which it is by default), the DHT will only be used for torrents where all trackers in its tracker list has failed. Either by an explicit error diff --git a/docs/manual.rst b/docs/manual.rst index 0ff1c21ef..285a29b60 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -2112,6 +2112,8 @@ that will be sent to the tracker. The user-agent is a good way to identify your int urlseed_pipeline_size; int file_pool_size; bool allow_multiple_connections_per_ip; + int max_failcount; + int min_reconnect_time; bool use_dht_as_fallback; }; @@ -2205,6 +2207,14 @@ abusive behavior by peers. It may be useful to allow such connections in cases where simulations are run on the same machie, and all peers in a swarm has the same IP address. +``max_failcount`` is the maximum times we try to connect to a peer before +stop connecting again. If a peer succeeds, the failcounter is reset. If +a peer is retrieved from a peer source (other than DHT) the failcount is +decremented by one, allowing another try. + +``min_reconnect_time`` is the time to wait between connection attempts. If +the peer fails, the time is multiplied by fail counter. + ``use_dht_as_fallback`` determines how the DHT is used. If this is true (which it is by default), the DHT will only be used for torrents where all trackers in its tracker list has failed. Either by an explicit error diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index 2c7cbeb6e..5a5b87626 100755 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -130,6 +130,12 @@ namespace libtorrent // decreases the peer count for the given piece // (used when a peer disconnects) void dec_refcount(int index); + + // these will increase and decrease the peer count + // of all pieces. They are used when seeds join + // or leave the swarm. + void inc_refcount_all(); + void dec_refcount_all(); // This indicates that we just received this piece // it means that the refcounter will indicate that diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 46a16538d..ec97ac90f 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -349,6 +349,21 @@ namespace libtorrent } #endif } + + void peer_has_all() + { + if (m_picker.get()) + { + assert(!is_seed()); + m_picker->inc_refcount_all(); + } +#ifndef NDEBUG + else + { + assert(is_seed()); + } +#endif + } // when peer disconnects, this is called for every piece it had void peer_lost(int index) diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index 3f70f216f..aa809e746 100755 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -1259,6 +1259,8 @@ namespace libtorrent if (m_supports_extensions) write_extensions(); #endif + // consider this a successful connection, reset the failcount + if (peer_info_struct()) peer_info_struct()->failcount = 0; m_state = read_packet_size; reset_recv_buffer(4); } diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 8888f5ca5..0d4005888 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -260,41 +260,46 @@ namespace libtorrent // now that we have a piece_picker, // update it with this peers pieces - // build a vector of all pieces - m_num_pieces = 0; - bool interesting = false; - for (int i = 0; i < int(m_have_piece.size()); ++i) - { - if (m_have_piece[i]) - { - ++m_num_pieces; - t->peer_has(i); - // if the peer has a piece and we don't, the peer is interesting - if (!t->have_piece(i) - && t->picker().piece_priority(i) != 0) - interesting = true; - } - } - - if (m_num_pieces == int(m_have_piece.size())) + int num_pieces = std::count(m_have_piece.begin(), m_have_piece.end(), true); + if (num_pieces == int(m_have_piece.size())) { #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " *** THIS IS A SEED ***\n"; #endif - assert(m_peer_info); - m_peer_info->seed = true; + // if this is a web seed. we don't have a peer_info struct + if (m_peer_info) m_peer_info->seed = true; // if we're a seed too, disconnect if (t->is_seed()) { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " we're also a seed, disconnecting\n"; -#endif throw std::runtime_error("seed to seed connection redundant, disconnecting"); } + std::fill(m_have_piece.begin(), m_have_piece.end(), true); + m_num_pieces = num_pieces; + t->peer_has_all(); + if (!t->is_finished()) + t->get_policy().peer_is_interesting(*this); + return; } - if (interesting) - t->get_policy().peer_is_interesting(*this); + m_num_pieces = num_pieces; + // if we're a seed, we don't keep track of piece availability + if (!t->is_seed()) + { + bool interesting = false; + for (int i = 0; i < int(m_have_piece.size()); ++i) + { + if (m_have_piece[i]) + { + t->peer_has(i); + // if the peer has a piece and we don't, the peer is interesting + if (!t->have_piece(i) + && t->picker().piece_priority(i) != 0) + interesting = true; + } + } + if (interesting) + t->get_policy().peer_is_interesting(*this); + } } peer_connection::~peer_connection() @@ -795,32 +800,61 @@ namespace libtorrent { throw protocol_error("seed to seed connection redundant, disconnecting"); } + + std::fill(m_have_piece.begin(), m_have_piece.end(), true); + m_num_pieces = num_pieces; + t->peer_has_all(); + if (!t->is_finished()) + t->get_policy().peer_is_interesting(*this); + return; } // let the torrent know which pieces the // peer has - bool interesting = false; - for (int i = 0; i < (int)m_have_piece.size(); ++i) + // if we're a seed, we don't keep track of piece availability + if (!t->is_seed()) { - bool have = bitfield[i]; - if (have && !m_have_piece[i]) + bool interesting = false; + for (int i = 0; i < (int)m_have_piece.size(); ++i) { - m_have_piece[i] = true; - ++m_num_pieces; - t->peer_has(i); - if (!t->have_piece(i) && t->picker().piece_priority(i) != 0) - interesting = true; + bool have = bitfield[i]; + if (have && !m_have_piece[i]) + { + m_have_piece[i] = true; + ++m_num_pieces; + t->peer_has(i); + if (!t->have_piece(i) && t->picker().piece_priority(i) != 0) + interesting = true; + } + else if (!have && m_have_piece[i]) + { + // this should probably not be allowed + m_have_piece[i] = false; + --m_num_pieces; + t->peer_lost(i); + } } - else if (!have && m_have_piece[i]) + + if (interesting) t->get_policy().peer_is_interesting(*this); + } + else + { + for (int i = 0; i < (int)m_have_piece.size(); ++i) { - // this should probably not be allowed - m_have_piece[i] = false; - --m_num_pieces; - t->peer_lost(i); + bool have = bitfield[i]; + if (have && !m_have_piece[i]) + { + m_have_piece[i] = true; + ++m_num_pieces; + } + else if (!have && m_have_piece[i]) + { + // this should probably not be allowed + m_have_piece[i] = false; + --m_num_pieces; + } } } - - if (interesting) t->get_policy().peer_is_interesting(*this); } // ----------------------------- diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index d2b0c2d36..569e9105e 100755 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -249,12 +249,12 @@ namespace libtorrent */ } #endif -/* + if (i->index == piece_pos::we_have_index) { assert(t == 0 || t->have_piece(index)); assert(i->downloading == 0); - +/* // make sure there's no entry // with this index. (there shouldn't // be since the piece_map is piece_pos::we_have_index) @@ -265,6 +265,7 @@ namespace libtorrent assert(m_piece_info[i][j] != index); } } +*/ } else { @@ -278,7 +279,7 @@ namespace libtorrent assert (i->index < vec.size()); assert(vec[i->index] == index); } - +/* for (int k = 0; k < int(m_piece_info.size()); ++k) { for (int j = 0; j < int(m_piece_info[k].size()); ++j) @@ -287,8 +288,9 @@ namespace libtorrent || (prio > 0 && prio == k && int(i->index) == j)); } } - } */ + } + int count = std::count_if(m_downloads.begin(), m_downloads.end() , has_index(index)); if (i->downloading == 1) @@ -589,6 +591,133 @@ namespace libtorrent } } + void piece_picker::inc_refcount_all() + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(m_files_checked_called); + + // in general priority = availability * 2 + // see piece_block::priority() + + // this will insert two empty vectors at the start of the + // piece_info vector. It is done like this as an optimization, + // to swap vectors instead of copying them + while (m_piece_info.size() < 3 + || (!m_piece_info.rbegin()->empty()) + || (!(m_piece_info.rbegin()+1)->empty())) + { + m_piece_info.push_back(std::vector()); + } + assert(m_piece_info.rbegin()->empty()); + assert((m_piece_info.rbegin()+1)->empty()); + typedef std::vector > piece_info_t; + for (piece_info_t::reverse_iterator i = m_piece_info.rbegin(), j(i+1) + , k(j+1), end(m_piece_info.rend()); k != end; ++i, ++j, ++k) + { + k->swap(*i); + } + assert(m_piece_info.begin()->empty()); + assert((m_piece_info.begin()+1)->empty()); + + // now, increase the peer count of all the pieces. + // because of different priorities, some pieces may have + // ended up in the wrong priority bucket. Adjust that. + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int prev_prio = i->priority(m_sequenced_download_threshold); + ++i->peer_count; + // if the assumption that the priority would + // increase by 2 when increasing the availability + // by one isn't true for this particular piece, correct it. + // that assumption is true for all pieces with priority 0 or 1 + int new_prio = i->priority(m_sequenced_download_threshold); + if (prev_prio == 0 && new_prio > 0) + { + add(i - m_piece_map.begin()); + continue; + } + if (new_prio == 0) + { + assert(prev_prio == 0); + continue; + } + if (new_prio == prev_prio + 2) + continue; + move(prev_prio + 2, i->index); + } + } + + void piece_picker::dec_refcount_all() + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(m_files_checked_called); + + assert(m_piece_info.size() >= 2); + assert(m_piece_info.front().empty()); + // swap all vectors two steps down + if (m_piece_info.size() > 2) + { + typedef std::vector > piece_info_t; + for (piece_info_t::iterator i = m_piece_info.begin(), j(i+1) + , k(j+1), end(m_piece_info.end()); k != end; ++i, ++j, ++k) + { + k->swap(*i); + } + } + else + { + m_piece_info.resize(3); + } + if (m_piece_info.size() & 1 == 0) + { + // if there's an even number of vectors, swap + // the last two to get the same layout in both cases + m_piece_info.rend()->swap(*(m_piece_info.rend()+1)); + } + assert(m_piece_info.back().empty()); + + // first is the vector that were + // bumped down to 0. The should always be moved + // since they have to be removed or reinserted + std::vector().swap(m_piece_info.front()); + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int prev_prio = i->priority(m_sequenced_download_threshold); + assert(i->peer_count > 0); + --i->peer_count; + // if the assumption that the priority would + // decrease by 2 when decreasing the availability + // by one isn't true for this particular piece, correct it. + // that assumption is true for all pieces with priority 0 or 1 + if (prev_prio == 0) + { + assert(i->priority(m_sequenced_download_threshold) == 0); + continue; + } + + int new_prio = i->priority(m_sequenced_download_threshold); + if (new_prio == prev_prio - 2) continue; + + // if this piece was pushed down to priority 0, it was + // removed + if (prev_prio == 2) + { + assert(new_prio > 0); + add(i - m_piece_map.begin()); + continue; + } + + // if this piece was one of the vectors that was pushed to the + // top, adjust the prev_prio to point to that vector, so that + // the pieces are moved from there + if (prev_prio == 1) prev_prio = m_piece_info.size(); + move(prev_prio - 2, i->index); + } + } + void piece_picker::inc_refcount(int i) { // TORRENT_PIECE_PICKER_INVARIANT_CHECK; diff --git a/src/torrent.cpp b/src/torrent.cpp index b82f7ea8a..c1e12aafb 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1291,12 +1291,27 @@ namespace libtorrent { assert(p->associated_torrent().lock().get() == this); - const std::vector& pieces = p->get_bitfield(); - - for (std::vector::const_iterator i = pieces.begin(); - i != pieces.end(); ++i) + if (p->is_seed()) { - if (*i) peer_lost(static_cast(i - pieces.begin())); + if (m_picker.get()) + { + assert(!is_seed()); + m_picker->dec_refcount_all(); + } + } + else + { + // if we're a seed, we don't keep track of piece availability + if (!is_seed()) + { + const std::vector& pieces = p->get_bitfield(); + + for (std::vector::const_iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + if (*i) peer_lost(static_cast(i - pieces.begin())); + } + } } }