diff --git a/ChangeLog b/ChangeLog index 4c2fe72b2..b569bafc1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ + * improve piece picker support for reverse picking (used for snubbed peers) + to not cause priority inversion for regular peers * improve piece picker to better support torrents with very large pieces and web seeds. (request large contiguous ranges, but not necessarily a whole piece). diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index 077ca189a..1e577945b 100644 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -53,7 +53,10 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/assert.hpp" #include "libtorrent/time.hpp" +// this is really only useful for debugging unit tests //#define TORRENT_PICKER_LOG + +// heavy weight reference counting invariant checks //#define TORRENT_DEBUG_REFCOUNTS #ifdef TORRENT_DEBUG_REFCOUNTS @@ -112,7 +115,7 @@ namespace libtorrent // the number of priority levels priority_levels = 8, // priority factor - prio_factor = priority_levels - 4 + prio_factor = 3 }; struct block_info @@ -139,6 +142,8 @@ namespace libtorrent // are considered fast peers or slow peers. // none is set if the blocks were downloaded // in a previous session + // TODO:3 perhaps the piece_state feature could be removed. It's not + // obvious that it has any effect enum piece_state_t { none, slow, medium, fast }; @@ -354,8 +359,10 @@ namespace libtorrent bool is_finished(piece_block block) const; // marks this piece-block as queued for downloading + // options are flags from options_t. bool mark_as_downloading(piece_block block, void* peer - , piece_state_t s); + , piece_state_t s, int options = 0); + // returns true if the block was marked as writing, // and false if the block is already finished or writing bool mark_as_writing(piece_block block, void* peer); @@ -418,7 +425,8 @@ namespace libtorrent std::vector get_download_queue() const; int get_download_queue_size() const; - void get_download_queue_sizes(int* partial, int* full, int* finished, int* zero_prio) const; + void get_download_queue_sizes(int* partial + , int* full, int* finished, int* zero_prio) const; void* get_downloader(piece_block block) const; @@ -454,7 +462,7 @@ namespace libtorrent void check_peer_invariant(bitfield const& have, void const* p) const; void check_invariant(const torrent* t = 0) const; #endif -#if defined TORRENT_PICKER_LOG +#if defined TORRENT_PICKER_LOG || defined TORRENT_DEBUG void print_pieces() const; #endif @@ -488,30 +496,86 @@ namespace libtorrent piece_pos() {} piece_pos(int peer_count_, int index_) : peer_count(peer_count_) - , state(piece_pos::piece_open) - , piece_priority(1) + , download_state(piece_pos::piece_open) + , piece_priority(4) , index(index_) { TORRENT_ASSERT(peer_count_ >= 0); TORRENT_ASSERT(index_ >= 0); } - // state of this piece. + // download_state of this piece. enum state_t { - // the piece is open to be picked - piece_open, // the piece is partially downloaded or requested piece_downloading, - // all blocks in the piece have been requested + // partial pieces where all blocks in the piece have been requested piece_full, - // all blocks in the piece have been received and - // are either finished or writing + // partial pieces where all blocks in the piece have been received + // and are either finished or writing piece_finished, - // pieces whose priority is 0 - piece_zero_prio + // partial pieces whose priority is 0 + piece_zero_prio, + + // the states up to this point indicate the piece is being + // downloaded (or at least has a partially downloaded piece + // in one of the m_downloads buckets). + num_download_categories, + + // the piece is open to be picked + piece_open = num_download_categories, + + // this is not a new download category/download list bucket. + // it still goes into the piece_downloading bucket. However, + // it indicates that this piece only has outstanding requests + // form reverse peers. This is to de-prioritize it somewhat + piece_downloading_reverse, + piece_full_reverse }; + // returns one of the valid download categories of state_t or + // piece_open if this piece is not being downloaded + int download_queue() const + { + if (download_state == piece_downloading_reverse) + return piece_downloading; + if (download_state == piece_full_reverse) + return piece_full; + return download_state; + } + + bool reverse() const + { + return download_state == piece_downloading_reverse + || download_state == piece_full_reverse; + } + + void unreverse() + { + switch (download_state) + { + case piece_downloading_reverse: + download_state = piece_downloading; + break; + case piece_full_reverse: + download_state = piece_full; + break; + } + } + + void make_reverse() + { + switch (download_state) + { + case piece_downloading: + download_state = piece_downloading_reverse; + break; + case piece_full: + download_state = piece_full_reverse; + break; + } + } + // the number of peers that has this piece // (availability) #if TORRENT_OPTIMIZE_MEMORY_USAGE @@ -520,16 +584,31 @@ namespace libtorrent boost::uint32_t peer_count : 16; #endif - boost::uint32_t state : 3; + // one of the enums from state_t. This indicates whether this piece + // is currently being downloaded or not, and what state it's in if + // it is. Specifically, as an optimization, pieces that have all blocks + // requested from them are separated out into separate lists to make + // lookups quicker. The main oddity is that whether a downloading piece + // has only been requested from peers that are reverse, that's + // recorded as piece_downloading_reverse, which really means the same + // as piece_downloading, it just saves space to also indicate that it + // has a bit lower priority. The reverse bit is only relevant if the + // state is piece_downloadin. + boost::uint32_t download_state : 3; + + // TODO: 2 having 8 priority levels is probably excessive. It should + // probably be changed to 3 levels + dont-download // is 0 if the piece is filtered (not to be downloaded) - // 1 is normal priority (default) - // 2 is higher priority than pieces at the same availability level - // 3 is same priority as partial pieces - // 4 is higher priority than partial pieces - // 5 and 6 same priority as availability 1 (ignores availability) - // 7 is maximum priority (ignores availability) + // 1 is low priority + // 2 is low priority + // 3 is mid priority + // 4 is default priority + // 5 is mid priority + // 6 is high priority + // 7 is high priority boost::uint32_t piece_priority : 3; + // index in to the piece_info vector #if TORRENT_OPTIMIZE_MEMORY_USAGE boost::uint32_t index : 17; @@ -565,22 +644,29 @@ namespace libtorrent bool have() const { return index == we_have_index; } void set_have() { index = we_have_index; TORRENT_ASSERT(have()); } void set_not_have() { index = 0; TORRENT_ASSERT(!have()); } - bool downloading() const { return state > 0; } + bool downloading() const { return download_state != piece_open; } bool filtered() const { return piece_priority == filter_priority; } - // prio 7 is always top priority - // prio 0 is always -1 (don't pick) - // downloading pieces are always on an even prio_factor priority + // this function returns the effective priority of the piece. It's + // actually the sort order of this piece compared to other pieces. A + // lower index means it will be picked before a piece with a higher + // index. + // The availability of the piece (the number of peers that have this + // piece) is fundamentally controlling the priority. It's multiplied + // by 3 to form 3 levels of priority for each availability. // - // availability x, downloading - // | availability x, prio 3; availability 2x, prio 6 - // | | availability x, prio 2; availability 2x, prio 5 - // | | | availability x, prio 1; availability 2x, prio 4 - // | | | | - // +---+---+---+---+ - // | 0 | 1 | 2 | 3 | - // +---+---+---+---+ + // downloading pieces (not reverse) + // | open pieces (not downloading) + // | | downloading pieces (reverse peers) + // | | | + // +---+---+---+ + // | 0 | 1 | 2 | + // +---+---+---+ + // this '3' is called prio_factor + // + // the manually set priority takes presedence over the availability + // by multiplying availability by priority. int priority(piece_picker const* picker) const { @@ -588,23 +674,28 @@ namespace libtorrent // availability = 0 should not be present in the piece list // returning -1 indicates that they shouldn't. if (filtered() || have() || peer_count + picker->m_seeds == 0 - || state == piece_full || state == piece_finished) + || download_state == piece_full + || download_state == piece_finished) return -1; - // prio 7 disregards availability - if (piece_priority == priority_levels - 1) return 1 - downloading(); + TORRENT_ASSERT(piece_priority > 0); - // prio 4,5,6 halves the availability of a piece - int availability = peer_count; - int p = piece_priority; - if (piece_priority >= priority_levels / 2) - { - availability /= 2; - p -= (priority_levels - 2) / 2; - } + // this is to keep downloading pieces at higher priority than + // pieces that are not being downloaded, and to make reverse + // downloading pieces to be lower priority + int adjustment = -2; + if (reverse()) adjustment = -1; + else if (download_state != piece_open) adjustment = -3; - if (downloading()) return availability * prio_factor; - return availability * prio_factor + (priority_levels / 2) - p; + // the + 1 here is because peer_count count be 0, it m_seeds + // is > 0. We don't actually care about seeds (except for the + // first one) since the order of the pieces is unaffected. + int availability = int(peer_count) + 1; + TORRENT_ASSERT(availability > 0); + TORRENT_ASSERT(int(priority_levels - piece_priority) > 0); + + return availability * int(priority_levels - piece_priority) + * prio_factor + adjustment; } bool operator!=(piece_pos p) const @@ -654,10 +745,9 @@ namespace libtorrent // returns an iterator to the downloading piece, whichever // download list it may live in now - std::vector::iterator update_piece_state(std::vector::iterator dp); + std::vector::iterator update_piece_state( + std::vector::iterator dp); - // some compilers (e.g. gcc 2.95, does not inherit access - // privileges to nested classes) private: // the following vectors are mutable because they sometimes may @@ -689,21 +779,15 @@ namespace libtorrent // 0, priority 1 starts at m_priority_boundries[0] etc. mutable std::vector m_priority_boundries; - // each piece that's currently being downloaded - // has an entry in this list with block allocations. - // i.e. it says wich parts of the piece that - // is being downloaded. This list is ordered - // by piece index to make lookups efficient - // there are 3 buckets of downloading pieces, each - // is individually sorted by piece index. - // 0: downloading pieces with unrequested blocks - // 1: downloading pieces where every block is busy - // and some are still in the requested state - // 2: downloading pieces where every block is - // finished or writing - // 3: partial pieces whose priority is 0 - enum { num_download_categories = 4 }; - std::vector m_downloads[num_download_categories]; + // each piece that's currently being downloaded has an entry in this list + // with block allocations. i.e. it says wich parts of the piece that is + // being downloaded. This list is ordered by piece index to make lookups + // efficient there are as many buckets as there are piece states. See + // piece_pos::state_t. The only download state that does not have a + // corresponding downloading_piece vector is piece_open and + // piece_downloading_reverse (the latter uses the same as + // piece_downloading). + std::vector m_downloads[piece_pos::num_download_categories]; // this holds the information of the // blocks in partially downloaded pieces. diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 3592c8ef6..cb038dcb0 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -943,26 +943,13 @@ namespace libtorrent void piece_availability(std::vector& avail) const; // These functions are used to set and get the prioritiy of individual - // pieces. By default all pieces have priority 1. That means that the + // pieces. By default all pieces have priority 4. That means that the // random rarest first algorithm is effectively active for all pieces. // You may however change the priority of individual pieces. There are 8 - // different priority levels: - // - // 0. piece is not downloaded at all - // 1. normal priority. Download order is dependent on availability - // 2. higher than normal priority. Pieces are preferred over pieces with - // the same availability, but not over pieces with lower availability - // 3. pieces are as likely to be picked as partial pieces. - // 4. pieces are preferred over partial pieces, but not over pieces with - // lower availability - // 5. *currently the same as 4* - // 6. piece is as likely to be picked as any piece with availability 1 - // 7. maximum priority, availability is disregarded, the piece is - // preferred over any other piece with lower priority - // - // The exact definitions of these priorities are implementation details, - // and subject to change. The interface guarantees that higher number - // means higher priority, and that 0 means do not download. + // priority levels. 0 means not to download the piece at all. Otherwise, + // lower priority values means less likely to be picked. Piece priority + // takes presedence over piece availability. Every priority 7 piece will + // be attempted to be picked before a priority 6 piece and so on. // // ``piece_priority`` sets or gets the priority for an individual piece, // specified by ``index``. diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 444d62a6d..6addd0c70 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -3367,7 +3367,8 @@ namespace libtorrent } } - if (!t->picker().mark_as_downloading(block, peer_info_struct(), state)) + if (!t->picker().mark_as_downloading(block, peer_info_struct(), state + , picker_options())) { #if defined TORRENT_LOGGING peer_log("*** PIECE_PICKER [ not_picking: %d,%d failed to mark_as_downloading ]" diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index f488f17cb..8cb32856c 100644 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -102,7 +102,7 @@ namespace libtorrent m_reverse_cursor = int(m_piece_map.size()); m_cursor = 0; - for (int i = 0; i < num_download_categories; ++i) + for (int i = 0; i < piece_pos::num_download_categories; ++i) m_downloads[i].clear(); m_block_info.clear(); @@ -115,7 +115,7 @@ namespace libtorrent , end(m_piece_map.end()); i != end; ++i) { i->peer_count = 0; - i->state = piece_pos::piece_open; + i->download_state = piece_pos::piece_open; i->index = 0; #ifdef TORRENT_DEBUG_REFCOUNTS i->have_peers.clear(); @@ -149,11 +149,11 @@ namespace libtorrent TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < int(m_piece_map.size())); - int state = m_piece_map[index].state; - if (state > piece_pos::piece_open) + int state = m_piece_map[index].download_queue(); + if (state != piece_pos::piece_open) { - std::vector::const_iterator piece = find_dl_piece(state - 1, index); - TORRENT_ASSERT(piece != m_downloads[state - 1].end()); + std::vector::const_iterator piece = find_dl_piece(state, index); + TORRENT_ASSERT(piece != m_downloads[state].end()); st = *piece; return; } @@ -178,7 +178,8 @@ namespace libtorrent #endif int num_downloads = 0; - for (int k = 0; k < num_download_categories; ++k) num_downloads += m_downloads[k].size(); + for (int k = 0; k < piece_pos::num_download_categories; ++k) + num_downloads += m_downloads[k].size(); int block_index = num_downloads * m_blocks_per_piece; if (int(m_block_info.size()) < block_index + m_blocks_per_piece) @@ -188,7 +189,7 @@ namespace libtorrent m_block_info.resize(block_index + m_blocks_per_piece); if (base != NULL && &m_block_info[0] != base) { - for (int k = 0; k < num_download_categories; ++k) + for (int k = 0; k < piece_pos::num_download_categories; ++k) { // this means the memory was reallocated, update the pointers for (int i = 0; i < int(m_downloads[k].size()); ++i) @@ -203,9 +204,11 @@ namespace libtorrent // always insert into bucket 0 (piece_downloading) downloading_piece ret; ret.index = piece; - std::vector::iterator i = std::lower_bound(m_downloads[0].begin() - , m_downloads[0].end(), ret); - TORRENT_ASSERT(i == m_downloads[0].end() || i->index != piece); + int download_state = piece_pos::piece_downloading; + std::vector::iterator i + = std::lower_bound(m_downloads[download_state].begin() + , m_downloads[download_state].end(), ret); + TORRENT_ASSERT(i == m_downloads[download_state].end() || i->index != piece); ret.info = &m_block_info[block_index]; TORRENT_ASSERT(ret.info >= &m_block_info[0]); TORRENT_ASSERT(ret.info < &m_block_info[0] + m_block_info.size()); @@ -230,7 +233,7 @@ namespace libtorrent VALGRIND_CHECK_VALUE_IS_DEFINED(ret.info); VALGRIND_CHECK_VALUE_IS_DEFINED(ret.index); #endif - i = m_downloads[0].insert(i, ret); + i = m_downloads[download_state].insert(i, ret); #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); @@ -244,20 +247,19 @@ namespace libtorrent check_piece_state(); #endif - int state = m_piece_map[i->index].state; - TORRENT_ASSERT(state > piece_pos::piece_open); - int queue = state - 1; - TORRENT_ASSERT(find_dl_piece(queue, i->index) == i); + int download_state = m_piece_map[i->index].download_queue(); + TORRENT_ASSERT(download_state != piece_pos::piece_open); + TORRENT_ASSERT(find_dl_piece(download_state, i->index) == i); #if TORRENT_USE_ASSERTS - int prev_size = m_downloads[queue].size(); + int prev_size = m_downloads[download_state].size(); #endif int total_downloading_pieces = 0; - for (int k = 0; k < num_download_categories; ++k) total_downloading_pieces += m_downloads[k].size(); + for (int k = 0; k < piece_pos::num_download_categories; ++k) total_downloading_pieces += m_downloads[k].size(); std::vector::iterator other; bool found = false; - for (int k = 0; k < num_download_categories; ++k) + for (int k = 0; k < piece_pos::num_download_categories; ++k) { other = std::find_if( m_downloads[k].begin(), m_downloads[k].end() @@ -276,10 +278,10 @@ namespace libtorrent std::copy(other->info, other->info + m_blocks_per_piece, i->info); other->info = i->info; } - m_piece_map[i->index].state = piece_pos::piece_open; - m_downloads[queue].erase(i); + m_piece_map[i->index].download_state = piece_pos::piece_open; + m_downloads[download_state].erase(i); - TORRENT_ASSERT(prev_size == m_downloads[queue].size() + 1); + TORRENT_ASSERT(prev_size == m_downloads[download_state].size() + 1); #if TORRENT_USE_INVARIANT_CHECKS check_piece_state(); @@ -293,7 +295,7 @@ namespace libtorrent #endif std::vector ret; - for (int k = 0; k < num_download_categories; ++k) + for (int k = 0; k < piece_pos::num_download_categories; ++k) ret.insert(ret.end(), m_downloads[k].begin(), m_downloads[k].end()); return ret; } @@ -301,17 +303,18 @@ namespace libtorrent int piece_picker::get_download_queue_size() const { int ret = 0; - for (int k = 0; k < num_download_categories; ++k) + for (int k = 0; k < piece_pos::num_download_categories; ++k) ret += m_downloads[k].size(); return ret; } - void piece_picker::get_download_queue_sizes(int* partial, int* full, int* finished, int* zero_prio) const + void piece_picker::get_download_queue_sizes(int* partial + , int* full, int* finished, int* zero_prio) const { - *partial = m_downloads[0].size(); - *full = m_downloads[1].size(); - *finished = m_downloads[2].size(); - *zero_prio = m_downloads[3].size(); + *partial = m_downloads[piece_pos::piece_downloading].size(); + *full = m_downloads[piece_pos::piece_full].size(); + *finished = m_downloads[piece_pos::piece_finished].size(); + *zero_prio = m_downloads[piece_pos::piece_zero_prio].size(); } #if TORRENT_USE_INVARIANT_CHECKS @@ -319,7 +322,7 @@ namespace libtorrent void piece_picker::check_piece_state() const { #ifndef TORRENT_DISABLE_INVARIANT_CHECKS - for (int k = 0; k < num_download_categories; ++k) + for (int k = 0; k < piece_pos::num_download_categories; ++k) { if (!m_downloads[k].empty()) { @@ -378,11 +381,17 @@ namespace libtorrent } } -#if defined TORRENT_PICKER_LOG +#if defined TORRENT_PICKER_LOG || defined TORRENT_DEBUG void piece_picker::print_pieces() const { - int limit = 10; + int limit = 20; std::cerr << "[" << this << "] "; + if (m_dirty) + { + std::cerr << " === dirty ===" << std::endl; + return; + } + for (std::vector::const_iterator i = m_priority_boundries.begin() , end(m_priority_boundries.end()); i != end; ++i) { @@ -395,7 +404,11 @@ namespace libtorrent for (std::vector::const_iterator i = m_pieces.begin() , end(m_pieces.end()); i != end; ++i, ++index) { - if (limit == 0) break; + if (limit == 0) + { + std::cerr << " ..."; + break; + } if (*i == -1) break; while (j != m_priority_boundries.end() && *j <= index) { @@ -438,7 +451,7 @@ namespace libtorrent TORRENT_ASSERT(m_num_filtered >= 0); TORRENT_ASSERT(m_seeds >= 0); - for (int k = 0; k < num_download_categories; ++k) + for (int k = 0; k < piece_pos::num_download_categories; ++k) { if (!m_downloads[k].empty()) { @@ -471,12 +484,12 @@ namespace libtorrent if (t != 0) TORRENT_ASSERT((int)m_piece_map.size() == t->torrent_file().num_pieces()); - for (int j = 0; j < num_download_categories; ++j) + for (int j = 0; j < piece_pos::num_download_categories; ++j) { for (std::vector::const_iterator i = m_downloads[j].begin() , end(m_downloads[j].end()); i != end; ++i) { - TORRENT_ASSERT(m_piece_map[i->index].state == j + 1); + TORRENT_ASSERT(m_piece_map[i->index].download_queue() == j); bool blocks_requested = false; int num_blocks = blocks_in_piece(i->index); int num_requested = 0; @@ -486,7 +499,9 @@ namespace libtorrent for (int k = 0; k < num_blocks; ++k) { TORRENT_ASSERT(i->info[k].piece_index == i->index); - TORRENT_ASSERT(i->info[k].peer == 0 || static_cast(i->info[k].peer)->in_use); + TORRENT_ASSERT(i->info[k].peer == 0 + || static_cast(i->info[k].peer)->in_use); + if (i->info[k].state == block_info::state_finished) { ++num_finished; @@ -510,7 +525,7 @@ namespace libtorrent } } - switch(j + 1) + switch(j) { case piece_pos::piece_downloading: TORRENT_ASSERT(!m_piece_map[i->index].filtered()); @@ -537,8 +552,9 @@ namespace libtorrent TORRENT_ASSERT(num_requested == i->requested); TORRENT_ASSERT(num_writing == i->writing); TORRENT_ASSERT(num_finished == i->finished); - if (m_piece_map[i->index].state > piece_pos::piece_downloading - && m_piece_map[i->index].state < piece_pos::piece_zero_prio) + + if (m_piece_map[i->index].download_queue() == piece_pos::piece_full + || m_piece_map[i->index].download_queue() == piece_pos::piece_finished) TORRENT_ASSERT(num_finished + num_writing + num_requested == num_blocks); } } @@ -550,10 +566,6 @@ namespace libtorrent TORRENT_ASSERT(m_reverse_cursor > m_cursor || (m_cursor == num_pieces && m_reverse_cursor == 0)); -#ifdef TORRENT_NO_EXPENSIVE_INVARIANT_CHECK - return; -#endif - if (!m_dirty) { TORRENT_ASSERT(!m_priority_boundries.empty()); @@ -568,6 +580,11 @@ namespace libtorrent } TORRENT_ASSERT(m_priority_boundries.back() == int(m_pieces.size())); } + +#ifdef TORRENT_NO_EXPENSIVE_INVARIANT_CHECK + return; +#endif + int index = 0; for (std::vector::const_iterator i = m_piece_map.begin() , end(m_piece_map.end()); i != end && (i->have() || i->filtered()); @@ -656,7 +673,19 @@ namespace libtorrent TORRENT_ASSERT(!t->have_piece(index)); int prio = p.priority(this); - TORRENT_ASSERT(prio == -1 || p.downloading() == (prio % piece_picker::prio_factor == 0)); +#if TORRENT_USE_ASSERTS + if (p.downloading()) + { + if (p.reverse()) + TORRENT_ASSERT(prio == -1 || (prio % piece_picker::prio_factor == 2)); + else + TORRENT_ASSERT(prio == -1 || (prio % piece_picker::prio_factor == 0)); + } + else + { + TORRENT_ASSERT(prio == -1 || (prio % piece_picker::prio_factor == 1)); + } +#endif if (!m_dirty) { @@ -678,20 +707,35 @@ namespace libtorrent } } - int count_downloading = std::count_if(m_downloads[0].begin(), m_downloads[0].end() + int count_downloading = std::count_if( + m_downloads[piece_pos::piece_downloading].begin() + , m_downloads[piece_pos::piece_downloading].end() , has_index(index)); - int count_full = std::count_if(m_downloads[1].begin(), m_downloads[1].end() - , has_index(index)); - int count_finished = std::count_if(m_downloads[2].begin(), m_downloads[2].end() - , has_index(index)); - TORRENT_ASSERT(i->state == piece_pos::piece_open - || i->state == piece_pos::piece_zero_prio - || count_downloading + count_full + count_finished == 1); - switch(i->state) + int count_full = std::count_if( + m_downloads[piece_pos::piece_full].begin() + , m_downloads[piece_pos::piece_full].end() + , has_index(index)); + + int count_finished = std::count_if( + m_downloads[piece_pos::piece_finished].begin() + , m_downloads[piece_pos::piece_finished].end() + , has_index(index)); + + int count_zero = std::count_if( + m_downloads[piece_pos::piece_zero_prio].begin() + , m_downloads[piece_pos::piece_zero_prio].end() + , has_index(index)); + + TORRENT_ASSERT(i->download_queue() == piece_pos::piece_open + || count_zero + count_downloading + count_full + + count_finished == 1); + + switch(i->download_queue()) { case piece_pos::piece_open: - TORRENT_ASSERT(count_downloading + count_full + count_finished == 0); + TORRENT_ASSERT(count_downloading + + count_full + count_finished + count_zero == 0); break; case piece_pos::piece_downloading: TORRENT_ASSERT(count_downloading == 1); @@ -702,6 +746,9 @@ namespace libtorrent case piece_pos::piece_finished: TORRENT_ASSERT(count_finished == 1); break; + case piece_pos::piece_zero_prio: + TORRENT_ASSERT(count_zero == 1); + break; }; } TORRENT_ASSERT(num_have == m_num_have); @@ -792,7 +839,7 @@ namespace libtorrent #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "add " << index << " (" << priority << ")" << std::endl; - std::cerr << "[" << this << "] " << " p: state: " << p.state + std::cerr << "[" << this << "] " << " p: state: " << p.download_state << " peer_count: " << p.peer_count << " prio: " << p.piece_priority << " index: " << p.index << std::endl; @@ -1045,13 +1092,13 @@ namespace libtorrent TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < (int)m_piece_map.size()); - int state = m_piece_map[index].state; - TORRENT_ASSERT(state != piece_pos::piece_open); - if (state == piece_pos::piece_open) return; + int download_state = m_piece_map[index].download_queue(); + TORRENT_ASSERT(download_state != piece_pos::piece_open); + if (download_state == piece_pos::piece_open) return; - std::vector::iterator i = find_dl_piece(state - 1, index); + std::vector::iterator i = find_dl_piece(download_state, index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + TORRENT_ASSERT(i != m_downloads[download_state].end()); TORRENT_ASSERT(i->info >= &m_block_info[0] && i->info < &m_block_info[0] + m_block_info.size()); @@ -1506,11 +1553,11 @@ namespace libtorrent void piece_picker::piece_passed(int index) { piece_pos& p = m_piece_map[index]; - int state = p.state; - if (state == piece_pos::piece_open) return; + int download_state = p.download_queue(); + if (download_state == piece_pos::piece_open) return; - std::vector::iterator i = find_dl_piece(state - 1, index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + std::vector::iterator i = find_dl_piece(download_state, index); + TORRENT_ASSERT(i != m_downloads[download_state].end()); TORRENT_ASSERT(i->locked == false); if (i->locked) return; @@ -1540,11 +1587,11 @@ namespace libtorrent { // even though we don't have the piece, it // might still have passed hash check - int state = p.state; - if (state == piece_pos::piece_open) return; + int download_state = p.download_queue(); + if (download_state == piece_pos::piece_open) return; std::vector::iterator i - = find_dl_piece(state - 1, index); + = find_dl_piece(download_state, index); if (i->passed_hash_check) { i->passed_hash_check = false; @@ -1602,11 +1649,11 @@ namespace libtorrent int priority = p.priority(this); TORRENT_ASSERT(priority < int(m_priority_boundries.size()) || m_dirty); - if (p.state != piece_pos::piece_open) + if (p.download_queue() != piece_pos::piece_open) { std::vector::iterator i - = find_dl_piece(p.state - 1, index); - TORRENT_ASSERT(i != m_downloads[p.state - 1].end()); + = find_dl_piece(p.download_queue(), index); + TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); // decrement num_passed here to compensate // for the unconditional increment further down if (i->passed_hash_check) --m_num_passed; @@ -1686,8 +1733,9 @@ namespace libtorrent #ifdef TORRENT_PICKER_LOG std::cerr << "[" << this << "] " << "set_piece_priority(" << index << ", " << new_piece_priority << ")" << std::endl; #endif + TORRENT_ASSERT(new_piece_priority >= 0); - TORRENT_ASSERT(new_piece_priority <= 7); + TORRENT_ASSERT(new_piece_priority < priority_levels); TORRENT_ASSERT(index >= 0); TORRENT_ASSERT(index < (int)m_piece_map.size()); @@ -1767,10 +1815,11 @@ namespace libtorrent p.piece_priority = new_piece_priority; int new_priority = p.priority(this); - if (p.state > 0) + if (p.downloading()) { - std::vector::iterator i = find_dl_piece(p.state - 1, index); - if (i != m_downloads[p.state - 1].end()) + std::vector::iterator i = find_dl_piece( + p.download_queue(), index); + if (i != m_downloads[p.download_queue()].end()) update_piece_state(i); } @@ -1841,21 +1890,18 @@ namespace libtorrent } } - // pieces describes which pieces the peer we're requesting from - // has. - // interesting_blocks is an out parameter, and will be filled - // with (up to) num_blocks of interesting blocks that the peer has. - // prefer_contiguous_blocks can be set if this peer should download - // whole pieces rather than trying to download blocks from the - // same piece as other peers. - // the void* is the pointer to the torrent_peer of the peer we're - // picking pieces from. This is used when downloading whole pieces, - // to only pick from the same piece the same peer is downloading - // from. state is supposed to be set to fast if the peer is downloading - // relatively fast, by some notion. Slow peers will prefer not - // to pick blocks from the same pieces as fast peers, and vice - // versa. Downloading pieces are marked as being fast, medium - // or slow once they're started. + // pieces describes which pieces the peer we're requesting from has. + // interesting_blocks is an out parameter, and will be filled with (up to) + // num_blocks of interesting blocks that the peer has. + // prefer_contiguous_blocks can be set if this peer should download whole + // pieces rather than trying to download blocks from the same piece as other + // peers. the void* is the pointer to the torrent_peer of the peer we're + // picking pieces from. This is used when downloading whole pieces, to only + // pick from the same piece the same peer is downloading from. + // download_state is supposed to be set to fast if the peer is downloading + // relatively fast, by some notion. Slow peers will prefer not to pick + // blocks from the same pieces as fast peers, and vice versa. Downloading + // pieces are marked as being fast, medium or slow once they're started. // options are: // * rarest_first @@ -1875,7 +1921,7 @@ namespace libtorrent // have an affinity to pick pieces in the same speed // category. - // only one of rarest_first, sequential can be set + // only one of rarest_first or sequential can be set void piece_picker::pick_pieces(bitfield const& pieces , std::vector& interesting_blocks, int num_blocks @@ -1891,7 +1937,8 @@ namespace libtorrent // make this scale by the number of peers we have. For large // scale clients, we would have more peers, and allow a higher // threshold for the number of partials - if (int(m_downloads[0].size()) > m_num_pad_files + num_peers * 3 / 2) + if (int(m_downloads[piece_pos::piece_downloading].size()) + > m_num_pad_files + num_peers * 3 / 2) { // if we have too many partial pieces, prioritize completing // them. In order for this to have an affect, also disable @@ -1929,16 +1976,19 @@ namespace libtorrent if (options & prioritize_partials) { - for (std::vector::const_iterator i = m_downloads[0].begin() - , end(m_downloads[0].end()); i != end; ++i) + for (std::vector::const_iterator i + = m_downloads[piece_pos::piece_downloading].begin() + , end(m_downloads[piece_pos::piece_downloading].end()); i != end; ++i) { - // in time critical mode, only pick prio 7 pieces - if ((options & time_critical_mode) && piece_priority(i->index) != 7) + // in time critical mode, only pick high priority pieces + if ((options & time_critical_mode) + && piece_priority(i->index) != priority_levels - 1) continue; pc.inc_stats_counter(counters::piece_picker_partial_loops); if (!is_piece_free(i->index, pieces)) continue; - TORRENT_ASSERT(m_piece_map[i->index].state == piece_pos::piece_downloading); + TORRENT_ASSERT(m_piece_map[i->index].download_queue() + == piece_pos::piece_downloading); if (int(backup_blocks.size()) >= num_blocks && int(backup_blocks2.size()) >= num_blocks) continue; @@ -1963,8 +2013,9 @@ namespace libtorrent for (std::vector::const_iterator i = suggested_pieces.begin(); i != suggested_pieces.end(); ++i) { - // in time critical mode, only pick prio 7 pieces - if ((options & time_critical_mode) && piece_priority(*i) != 7) + // in time critical mode, only pick high priority pieces + if ((options & time_critical_mode) + && piece_priority(*i) != priority_levels - 1) continue; pc.inc_stats_counter(counters::piece_picker_suggest_loops); @@ -1984,7 +2035,7 @@ namespace libtorrent TORRENT_ASSERT(!m_dirty); for (std::vector::const_iterator i = m_pieces.begin(); - i != m_pieces.end() && piece_priority(*i) == 7; ++i) + i != m_pieces.end() && piece_priority(*i) == priority_levels - 1; ++i) { if (!is_piece_free(*i, pieces)) continue; num_blocks = add_blocks(*i, pieces @@ -1995,7 +2046,7 @@ namespace libtorrent if (num_blocks <= 0) return; } - // in time critical mode, only pick prio 7 pieces + // in time critical mode, only pick high priority pieces if ((options & time_critical_mode) == 0) { if (options & reverse) @@ -2004,8 +2055,8 @@ namespace libtorrent { pc.inc_stats_counter(counters::piece_picker_sequential_loops); if (!is_piece_free(i, pieces)) continue; - // we've already added prio 7 pieces - if (piece_priority(i) == 7) continue; + // we've already added high priority pieces + if (piece_priority(i) == priority_levels - 1) continue; num_blocks = add_blocks(i, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks @@ -2020,8 +2071,8 @@ namespace libtorrent { pc.inc_stats_counter(counters::piece_picker_sequential_loops); if (!is_piece_free(i, pieces)) continue; - // we've already added prio 7 pieces - if (piece_priority(i) == 7) continue; + // we've already added high priority pieces + if (piece_priority(i) == priority_levels - 1) continue; num_blocks = add_blocks(i, pieces , interesting_blocks, backup_blocks , backup_blocks2, num_blocks @@ -2037,30 +2088,17 @@ namespace libtorrent if (m_dirty) update_pieces(); TORRENT_ASSERT(!m_dirty); - // in time critical mode, we're only allowed to pick prio 7 + // in time critical mode, we're only allowed to pick high priority // pieces. This is why reverse mode is disabled when we're in - // time-critical mode, because all prio 7 pieces are at the front - // of the list + // time-critical mode, because all high priority pieces are at the + // front of the list if ((options & reverse) && (options & time_critical_mode) == 0) { - // it's a bit complicated in order to always prioritize - // partial pieces, and respect priorities. Every chunk - // of 4 priority levels are traversed in forward order, but otherwise - // they are traversed in reverse order - // round up to an even 4 priority boundry, to make it simpler - // to do the akward reverse traversing -#define div_round_up(n, d) (((n) + (d) - 1) / (d)) - m_priority_boundries.resize(div_round_up(m_priority_boundries.size() - , prio_factor) * prio_factor, m_priority_boundries.back()); for (int i = m_priority_boundries.size() - 1; i >= 0; --i) { - int prio = (i / prio_factor) * prio_factor - + prio_factor - 1 - (i % prio_factor); - - TORRENT_ASSERT(prio >= 0); - TORRENT_ASSERT(prio < int(m_priority_boundries.size())); - int start = prio == 0 ? 0 : m_priority_boundries[prio - 1]; - for (int p = start; p < m_priority_boundries[prio]; ++p) + int start = (i == 0) ? 0 : m_priority_boundries[i - 1]; + int end = m_priority_boundries[i]; + for (int p = end - 1; p >= start; --p) { pc.inc_stats_counter(counters::piece_picker_reverse_rare_loops); @@ -2073,7 +2111,6 @@ namespace libtorrent if (num_blocks <= 0) return; } } -#undef div_round_up } else { @@ -2082,11 +2119,12 @@ namespace libtorrent { pc.inc_stats_counter(counters::piece_picker_rare_loops); - // in time critical mode, only pick prio 7 pieces + // in time critical mode, only pick high priority pieces // it's safe to break here because in this mode we // pick pieces in priority order. Once we hit a lower priority - // piece, we won't encounter any more prio 7 ones - if ((options & time_critical_mode) && piece_priority(*i) != 7) + // piece, we won't encounter any more high priority ones + if ((options & time_critical_mode) + && piece_priority(*i) != priority_levels - 1) break; if (!is_piece_free(*i, pieces)) continue; @@ -2103,9 +2141,9 @@ namespace libtorrent else if (options & time_critical_mode) { // if we're in time-critical mode, we are only allowed to pick - // prio 7 pieces. + // high priority pieces. for (std::vector::const_iterator i = m_pieces.begin(); - i != m_pieces.end() && piece_priority(*i) == 7; ++i) + i != m_pieces.end() && piece_priority(*i) == priority_levels - 1; ++i) { if (!is_piece_free(*i, pieces)) continue; num_blocks = add_blocks(*i, pieces @@ -2195,7 +2233,8 @@ namespace libtorrent // in end game mode we pick a single block // that has already been requested from someone // all pieces that are interesting are in - // m_downloads[0] and m_download[1] (i.e. partial and full pieces) + // m_downloads[0] and m_download[1] + // (i.e. partial and full pieces) std::vector temp; @@ -2205,8 +2244,9 @@ namespace libtorrent // this peer has, and can pick from. Cap the stack allocation // at 200 pieces. - int partials_size = (std::min)(200, int(m_downloads[0].size() - + m_downloads[1].size())); + int partials_size = (std::min)(200, int( + m_downloads[piece_pos::piece_downloading].size() + + m_downloads[piece_pos::piece_full].size())); if (partials_size == 0) return; downloading_piece const** partials @@ -2215,20 +2255,22 @@ namespace libtorrent #ifdef TORRENT_DEBUG for (std::vector::const_iterator i - = m_downloads[0].begin(), end(m_downloads[0].end()); i != end; ++i) + = m_downloads[piece_pos::piece_downloading].begin() + , end(m_downloads[piece_pos::piece_downloading].end()); i != end; ++i) { downloading_piece const& dp = *i; - if ((options & time_critical_mode) && piece_priority(dp.index) != 7) + if ((options & time_critical_mode) + && piece_priority(dp.index) != priority_levels - 1) continue; // we either don't have this piece, or we've already requested from it bool found = false; - for (std::vector::const_iterator i + for (std::vector::const_iterator j = interesting_blocks.begin(), end(interesting_blocks.end()); - i != end; ++i) + j != end; ++j) { - if (i->piece_index != dp.index) continue; + if (j->piece_index != dp.index) continue; found = true; break; } @@ -2237,7 +2279,8 @@ namespace libtorrent #endif for (std::vector::const_iterator i - = m_downloads[1].begin(), end(m_downloads[1].end()); + = m_downloads[piece_pos::piece_full].begin() + , end(m_downloads[piece_pos::piece_full].end()); i != end; ++i) { if (c == partials_size) break; @@ -2249,7 +2292,8 @@ namespace libtorrent // don't pick pieces with priority 0 TORRENT_ASSERT(piece_priority(dp.index) > 0); - if ((options & time_critical_mode) && piece_priority(dp.index) != 7) + if ((options & time_critical_mode) + && piece_priority(dp.index) != priority_levels - 1) continue; partials[c++] = &dp; @@ -2295,14 +2339,16 @@ namespace libtorrent // make sure that we at this point have added requests to all unrequested blocks // in all downloading pieces - for (std::vector::const_iterator i = m_downloads[0].begin() - , end(m_downloads[0].end()); i != end; ++i) + for (std::vector::const_iterator i + = m_downloads[piece_pos::piece_downloading].begin() + , end(m_downloads[piece_pos::piece_downloading].end()); i != end; ++i) { if (!pieces[i->index]) continue; if (piece_priority(i->index) == 0) continue; if (i->locked) continue; - - if ((options & time_critical_mode) && piece_priority(i->index) != 7) + + if ((options & time_critical_mode) + && piece_priority(i->index) != priority_levels - 1) continue; int num_blocks_in_piece = blocks_in_piece(i->index); @@ -2320,9 +2366,10 @@ namespace libtorrent for (k = interesting_blocks.begin(); k != interesting_blocks.end(); ++k) fprintf(stderr, "(%d, %d)", k->piece_index, k->block_index); fprintf(stderr, "\nnum_blocks: %d\n", num_blocks); - - for (std::vector::const_iterator l = m_downloads[0].begin() - , end(m_downloads[0].end()); l != end; ++l) + + for (std::vector::const_iterator l + = m_downloads[piece_pos::piece_downloading].begin() + , end(m_downloads[piece_pos::piece_downloading].end()); l != end; ++l) { fprintf(stderr, "%d : ", l->index); int num_blocks_in_piece = blocks_in_piece(l->index); @@ -2344,12 +2391,13 @@ namespace libtorrent if (m_piece_map[i].priority(this) <= 0) continue; if (have_piece(i)) continue; - int state = m_piece_map[i].state; - if (state == 0) continue; - std::vector::const_iterator k = find_dl_piece(state - 1, i); + int download_state = m_piece_map[i].download_queue(); + if (download_state == piece_pos::piece_open) continue; + std::vector::const_iterator k + = find_dl_piece(download_state, i); - TORRENT_ASSERT(k != m_downloads[state-1].end()); - if (k == m_downloads[state-1].end()) continue; + TORRENT_ASSERT(k != m_downloads[download_state].end()); + if (k == m_downloads[download_state].end()) continue; // this assert is not valid for web_seeds /* @@ -2499,16 +2547,21 @@ namespace libtorrent // ignore pieces found in the ignore list if (std::find(ignore.begin(), ignore.end(), piece) != ignore.end()) return num_blocks; - if (m_piece_map[piece].state > piece_pos::piece_downloading) return num_blocks; + if (m_piece_map[piece].download_queue() != piece_pos::piece_open + && m_piece_map[piece].download_queue() != piece_pos::piece_downloading) + return num_blocks; + TORRENT_ASSERT(m_piece_map[piece].priority(this) >= 0); - if (m_piece_map[piece].state == piece_pos::piece_downloading) + int state = m_piece_map[piece].download_queue(); + if (state == piece_pos::piece_downloading) { // if we're prioritizing partials, we've already // looked through the downloading pieces if (options & prioritize_partials) return num_blocks; - std::vector::const_iterator i = find_dl_piece(0, piece); - TORRENT_ASSERT(i != m_downloads[0].end()); + std::vector::const_iterator i = find_dl_piece( + piece_pos::piece_downloading, piece); + TORRENT_ASSERT(i != m_downloads[state].end()); // std::cout << "add_blocks_downloading(" << piece << ")" << std::endl; @@ -2728,16 +2781,15 @@ namespace libtorrent piece_pos const& p = m_piece_map[index]; if (p.index == piece_pos::we_have_index) return true; - int state = p.state; + int state = p.download_queue(); if (state == piece_pos::piece_open) { - TORRENT_ASSERT(find_dl_piece(0, index) == m_downloads[0].end()); - TORRENT_ASSERT(find_dl_piece(1, index) == m_downloads[1].end()); - TORRENT_ASSERT(find_dl_piece(2, index) == m_downloads[2].end()); + for (int i = 0; i < piece_pos::num_download_categories; ++i) + TORRENT_ASSERT(find_dl_piece(i, index) == m_downloads[i].end()); return false; } - std::vector::const_iterator i = find_dl_piece(state - 1,index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + std::vector::const_iterator i = find_dl_piece(state, index); + TORRENT_ASSERT(i != m_downloads[state].end()); TORRENT_ASSERT((int)i->finished <= m_blocks_per_piece); int max_blocks = blocks_in_piece(index); if (int(i->finished) + int(i->writing) < max_blocks) return false; @@ -2763,23 +2815,22 @@ namespace libtorrent piece_pos const& p = m_piece_map[index]; if (p.index == piece_pos::we_have_index) return true; - int state = p.state; + int state = p.download_queue(); if (state == piece_pos::piece_open) { - TORRENT_ASSERT(find_dl_piece(0, index) == m_downloads[0].end()); - TORRENT_ASSERT(find_dl_piece(1, index) == m_downloads[1].end()); - TORRENT_ASSERT(find_dl_piece(2, index) == m_downloads[2].end()); + for (int i = 0; i < piece_pos::num_download_categories; ++i) + TORRENT_ASSERT(find_dl_piece(i, index) == m_downloads[i].end()); return false; } - std::vector::const_iterator i = find_dl_piece(state - 1,index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + std::vector::const_iterator i = find_dl_piece(state, index); + TORRENT_ASSERT(i != m_downloads[state].end()); return i->passed_hash_check; } std::vector::iterator piece_picker::find_dl_piece( int queue, int index) { - TORRENT_ASSERT(queue >= 0 && queue < num_download_categories); + TORRENT_ASSERT(queue >= 0 && queue < piece_pos::num_download_categories); // return std::find_if(m_downloads[queue].begin(), m_downloads[queue].end(), has_index(index)); downloading_piece cmp; cmp.index = index; @@ -2793,7 +2844,7 @@ namespace libtorrent std::vector::const_iterator piece_picker::find_dl_piece( int queue, int index) const { - TORRENT_ASSERT(queue >= 0 && queue < num_download_categories); + TORRENT_ASSERT(queue >= 0 && queue < piece_pos::num_download_categories); // return std::find_if(m_downloads[queue].begin(), m_downloads[queue].end(), has_index(index)); downloading_piece cmp; cmp.index = index; @@ -2804,7 +2855,8 @@ namespace libtorrent return m_downloads[queue].end(); } - std::vector::iterator piece_picker::update_piece_state( + std::vector::iterator + piece_picker::update_piece_state( std::vector::iterator dp) { #ifdef TORRENT_PICKER_LOG @@ -2813,7 +2865,7 @@ namespace libtorrent int num_blocks = blocks_in_piece(dp->index); piece_pos& p = m_piece_map[dp->index]; - int current_state = p.state; + int current_state = p.download_state; TORRENT_ASSERT(current_state != piece_pos::piece_open); if (current_state == piece_pos::piece_open) return dp; @@ -2830,12 +2882,16 @@ namespace libtorrent } else if (dp->requested + dp->finished + dp->writing < num_blocks) { - new_state = piece_pos::piece_downloading; + new_state = p.reverse() + ? piece_pos::piece_downloading_reverse + : piece_pos::piece_downloading; } else if (dp->requested > 0) { TORRENT_ASSERT(dp->requested + dp->finished + dp->writing == num_blocks); - new_state = piece_pos::piece_full; + new_state = p.reverse() + ? piece_pos::piece_full_reverse + : piece_pos::piece_full; } else { @@ -2851,27 +2907,30 @@ namespace libtorrent // assert that the iterator that was passed-in in fact lives in // the correct list - TORRENT_ASSERT(find_dl_piece(current_state - 1, dp->index) == dp); + TORRENT_ASSERT(find_dl_piece(p.download_queue(), dp->index) == dp); // remove the download_piece from the list corresponding // to the old state downloading_piece dp_info = *dp; - m_downloads[current_state - 1].erase(dp); + m_downloads[p.download_queue()].erase(dp); + + int prio = p.priority(this); + p.download_state = new_state; +#ifdef TORRENT_PICKER_LOG + std::cerr << "[" << this << "] " << " " << dp_info.index << " state (" << current_state << " -> " << new_state << ")" << std::endl; +#endif // insert the download_piece in the list corresponding to // the new state downloading_piece cmp; cmp.index = dp_info.index; std::vector::iterator i = std::lower_bound( - m_downloads[new_state - 1].begin(), m_downloads[new_state - 1].end(), cmp); - TORRENT_ASSERT(i == m_downloads[new_state - 1].end() || i->index != dp_info.index); - i = m_downloads[new_state - 1].insert(i, dp_info); + m_downloads[p.download_queue()].begin() + , m_downloads[p.download_queue()].end(), cmp); + TORRENT_ASSERT(i == m_downloads[p.download_queue()].end() + || i->index != dp_info.index); + i = m_downloads[p.download_queue()].insert(i, dp_info); - int prio = p.priority(this); - p.state = new_state; -#ifdef TORRENT_PICKER_LOG - std::cerr << "[" << this << "] " << " " << dp_info.index << " state (" << current_state << " -> " << new_state << ")" << std::endl; -#endif if (!m_dirty) { if (prio == -1 && p.priority(this) != -1) add(dp_info.index); @@ -2891,11 +2950,12 @@ namespace libtorrent if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return block_info::state_finished; - int state = m_piece_map[block.piece_index].state; + int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return block_info::state_none; - std::vector::const_iterator i = find_dl_piece(state - 1, block.piece_index); + std::vector::const_iterator i = find_dl_piece(state + , block.piece_index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + TORRENT_ASSERT(i != m_downloads[state].end()); TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); return i->info[block.block_index].state; } @@ -2909,11 +2969,12 @@ namespace libtorrent TORRENT_ASSERT(block.piece_index != piece_block::invalid.piece_index); TORRENT_ASSERT(block.piece_index < m_piece_map.size()); - int state = m_piece_map[block.piece_index].state; + int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return false; - std::vector::const_iterator i = find_dl_piece(state - 1, block.piece_index); + std::vector::const_iterator i = find_dl_piece(state + , block.piece_index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + TORRENT_ASSERT(i != m_downloads[state].end()); TORRENT_ASSERT(i->info >= &m_block_info[0] && i->info < &m_block_info[0] + m_block_info.size()); TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); @@ -2930,10 +2991,11 @@ namespace libtorrent TORRENT_ASSERT(block.piece_index < m_piece_map.size()); if (m_piece_map[block.piece_index].index == piece_pos::we_have_index) return true; - int state = m_piece_map[block.piece_index].state; + int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return false; - std::vector::const_iterator i = find_dl_piece(state - 1, block.piece_index); - TORRENT_ASSERT(i != m_downloads[state-1].end()); + std::vector::const_iterator i = find_dl_piece(state + , block.piece_index); + TORRENT_ASSERT(i != m_downloads[state].end()); TORRENT_ASSERT(i->info >= &m_block_info[0] && i->info < &m_block_info[0] + m_block_info.size()); TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); @@ -2952,20 +3014,23 @@ namespace libtorrent piece_pos const& p = m_piece_map[block.piece_index]; if (p.index == piece_pos::we_have_index) return true; - if (p.state == piece_pos::piece_open) return false; - std::vector::const_iterator i = find_dl_piece(p.state - 1, block.piece_index); - TORRENT_ASSERT(i != m_downloads[p.state - 1].end()); + if (p.download_queue() == piece_pos::piece_open) return false; + std::vector::const_iterator i = find_dl_piece(p.download_queue() + , block.piece_index); + TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); TORRENT_ASSERT(i->info >= &m_block_info[0] && i->info < &m_block_info[0] + m_block_info.size()); TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); return i->info[block.block_index].state == block_info::state_finished; } + // options may be 0 or piece_picker::reverse bool piece_picker::mark_as_downloading(piece_block block - , void* peer, piece_state_t state) + , void* peer, piece_state_t state, int options) { #ifdef TORRENT_PICKER_LOG - std::cerr << "[" << this << "] " << "mark_as_downloading( {" << block.piece_index << ", " << block.block_index << "} )" << std::endl; + std::cerr << "[" << this << "] " << "mark_as_downloading( {" + << block.piece_index << ", " << block.block_index << "} )" << std::endl; #endif TORRENT_ASSERT(peer == 0 || static_cast(peer)->in_use); @@ -2977,7 +3042,7 @@ namespace libtorrent TORRENT_ASSERT(!m_piece_map[block.piece_index].have()); piece_pos& p = m_piece_map[block.piece_index]; - if (p.state == piece_pos::piece_open) + if (p.download_queue() == piece_pos::piece_open) { #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; @@ -2985,7 +3050,11 @@ namespace libtorrent int prio = p.priority(this); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); - p.state = piece_pos::piece_downloading; + + p.download_state = (options & reverse) + ? piece_pos::piece_downloading_reverse + : piece_pos::piece_downloading; + if (prio >= 0 && !m_dirty) update(prio, p.index); dlpiece_iter dp = add_download_piece(block.piece_index); @@ -3010,8 +3079,9 @@ namespace libtorrent #ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif - std::vector::iterator i = find_dl_piece(p.state - 1, block.piece_index); - TORRENT_ASSERT(i != m_downloads[p.state - 1].end()); + std::vector::iterator i = find_dl_piece(p.download_queue() + , block.piece_index); + TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info& info = i->info[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); @@ -3021,6 +3091,17 @@ namespace libtorrent { return false; } + + if ((options & reverse) && !p.reverse() && i->requested == 0) + { + // this piece isn't reverse, but there's no other peer + // downloading from it and we just requested a block from a + // reverse peer. Make it reverse + int prio = p.priority(this); + p.make_reverse(); + if (prio >= 0 && !m_dirty) update(prio, p.index); + } + TORRENT_ASSERT(info.state == block_info::state_none || (info.state == block_info::state_requested && (info.num_peers > 0))); @@ -3032,6 +3113,17 @@ namespace libtorrent i = update_piece_state(i); } ++info.num_peers; + + // if we make a non-reverse request from a reversed piece, + // undo the reverse state + if ((options & reverse) == 0 && p.reverse()) + { + int prio = p.priority(this); + // make it non-reverse + p.unreverse(); + if (prio >= 0 && !m_dirty) update(prio, p.index); + } + #if TORRENT_USE_ASSERTS TORRENT_ASSERT(info.peers.count(peer) == 0); info.peers.insert(peer); @@ -3051,8 +3143,9 @@ namespace libtorrent piece_pos const& p = m_piece_map[block.piece_index]; if (!p.downloading()) return 0; - std::vector::const_iterator i = find_dl_piece(p.state - 1, block.piece_index); - TORRENT_ASSERT(i != m_downloads[p.state - 1].end()); + std::vector::const_iterator i = find_dl_piece(p.download_queue() + , block.piece_index); + TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info const& info = i->info[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); @@ -3110,7 +3203,7 @@ namespace libtorrent int prio = p.priority(this); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); - p.state = piece_pos::piece_downloading; + p.download_state = piece_pos::piece_downloading; // prio being -1 can happen if a block is requested before // the piece priority was set to 0 if (prio >= 0 && !m_dirty) update(prio, p.index); @@ -3133,8 +3226,9 @@ namespace libtorrent } else { - std::vector::iterator i = find_dl_piece(p.state - 1, block.piece_index); - TORRENT_ASSERT(i != m_downloads[p.state - 1].end()); + std::vector::iterator i = find_dl_piece(p.download_queue() + , block.piece_index); + TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info& info = i->info[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); @@ -3186,10 +3280,10 @@ namespace libtorrent std::cerr << "[" << this << "] " << "lock_piece(" << piece << ")" << std::endl; #endif - int state = m_piece_map[piece].state; + int state = m_piece_map[piece].download_queue(); if (state == piece_pos::piece_open) return; - std::vector::iterator i = find_dl_piece(state - 1, piece); - if (i == m_downloads[state - 1].end()) return; + std::vector::iterator i = find_dl_piece(state, piece); + if (i == m_downloads[state].end()) return; TORRENT_ASSERT(i->passed_hash_check == false); if (i->passed_hash_check) @@ -3221,10 +3315,10 @@ namespace libtorrent std::cerr << "[" << this << "] " << "write_failed( {" << block.piece_index << ", " << block.block_index << "} )" << std::endl; #endif - int state = m_piece_map[block.piece_index].state; + int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return; - std::vector::iterator i = find_dl_piece(state - 1, block.piece_index); - if (i == m_downloads[state - 1].end()) return; + std::vector::iterator i = find_dl_piece(state, block.piece_index); + if (i == m_downloads[state].end()) return; block_info& info = i->info[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); @@ -3289,11 +3383,12 @@ namespace libtorrent piece_pos& p = m_piece_map[block.piece_index]; - if (p.state == piece_pos::piece_open) return; + if (p.download_queue() == piece_pos::piece_open) return; - std::vector::iterator i = find_dl_piece(p.state - 1, block.piece_index); + std::vector::iterator i = find_dl_piece(p.download_queue() + , block.piece_index); - TORRENT_ASSERT(i != m_downloads[p.state - 1].end()); + TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info& info = i->info[block.block_index]; if (info.state == block_info::state_finished) return; @@ -3336,7 +3431,7 @@ namespace libtorrent piece_pos& p = m_piece_map[block.piece_index]; - if (p.state == piece_pos::piece_open) + if (p.download_queue() == piece_pos::piece_open) { // if we already have this piece, just ignore this if (have_piece(block.piece_index)) return; @@ -3348,7 +3443,7 @@ namespace libtorrent int prio = p.priority(this); TORRENT_ASSERT(prio < int(m_priority_boundries.size()) || m_dirty); - p.state = piece_pos::piece_downloading; + p.download_state = piece_pos::piece_downloading; if (prio >= 0 && !m_dirty) update(prio, p.index); dlpiece_iter dp = add_download_piece(block.piece_index); @@ -3371,8 +3466,9 @@ namespace libtorrent TORRENT_PIECE_PICKER_INVARIANT_CHECK; #endif - std::vector::iterator i = find_dl_piece(p.state - 1, block.piece_index); - TORRENT_ASSERT(i != m_downloads[p.state - 1].end()); + std::vector::iterator i = find_dl_piece(p.download_queue() + , block.piece_index); + TORRENT_ASSERT(i != m_downloads[p.download_queue()].end()); block_info& info = i->info[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size()); @@ -3419,20 +3515,20 @@ namespace libtorrent void piece_picker::mark_as_checking(int index) { - int state = m_piece_map[index].state; + int state = m_piece_map[index].download_queue(); if (state == piece_pos::piece_open) return; - std::vector::iterator i = find_dl_piece(state - 1, index); - if (i == m_downloads[state - 1].end()) return; + std::vector::iterator i = find_dl_piece(state, index); + if (i == m_downloads[state].end()) return; TORRENT_ASSERT(i->outstanding_hash_check == false); i->outstanding_hash_check = true; } void piece_picker::mark_as_done_checking(int index) { - int state = m_piece_map[index].state; + int state = m_piece_map[index].download_queue(); if (state == piece_pos::piece_open) return; - std::vector::iterator i = find_dl_piece(state - 1, index); - if (i == m_downloads[state - 1].end()) return; + std::vector::iterator i = find_dl_piece(state, index); + if (i == m_downloads[state].end()) return; i->outstanding_hash_check = false; } @@ -3440,10 +3536,10 @@ namespace libtorrent { TORRENT_ASSERT(index >= 0 && index <= (int)m_piece_map.size()); - int state = m_piece_map[index].state; + int state = m_piece_map[index].download_queue(); TORRENT_ASSERT(state != piece_pos::piece_open); - std::vector::const_iterator i = find_dl_piece(state - 1, index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + std::vector::const_iterator i = find_dl_piece(state, index); + TORRENT_ASSERT(i != m_downloads[state].end()); d.clear(); for (int j = 0, end(blocks_in_piece(index)); j != end; ++j) @@ -3459,7 +3555,7 @@ namespace libtorrent TORRENT_ASSERT(index >= 0 && index <= (int)m_piece_map.size()); d.clear(); - int state = m_piece_map[index].state; + int state = m_piece_map[index].download_queue(); if (state == piece_pos::piece_open) { int num_blocks = blocks_in_piece(index); @@ -3467,8 +3563,8 @@ namespace libtorrent return; } - std::vector::const_iterator i = find_dl_piece(state - 1, index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + std::vector::const_iterator i = find_dl_piece(state, index); + TORRENT_ASSERT(i != m_downloads[state].end()); TORRENT_ASSERT(i->info >= &m_block_info[0] && i->info < &m_block_info[0] + m_block_info.size()); for (int j = 0, end(blocks_in_piece(index)); j != end; ++j) @@ -3480,10 +3576,11 @@ namespace libtorrent void* piece_picker::get_downloader(piece_block block) const { - int state = m_piece_map[block.piece_index].state; + int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return 0; - std::vector::const_iterator i = find_dl_piece(state - 1, block.piece_index); + std::vector::const_iterator i = find_dl_piece(state + , block.piece_index); TORRENT_ASSERT(block.block_index != piece_block::invalid.block_index); TORRENT_ASSERT(i->info[block.block_index].piece_index == block.piece_index); @@ -3513,11 +3610,12 @@ namespace libtorrent TORRENT_ASSERT(block.piece_index < m_piece_map.size()); TORRENT_ASSERT(int(block.block_index) < blocks_in_piece(block.piece_index)); - int state = m_piece_map[block.piece_index].state; + int state = m_piece_map[block.piece_index].download_queue(); if (state == piece_pos::piece_open) return; - std::vector::iterator i = find_dl_piece(state - 1, block.piece_index); - TORRENT_ASSERT(i != m_downloads[state - 1].end()); + std::vector::iterator i = find_dl_piece(state + , block.piece_index); + TORRENT_ASSERT(i != m_downloads[state].end()); block_info& info = i->info[block.block_index]; TORRENT_ASSERT(&info >= &m_block_info[0]); @@ -3582,7 +3680,7 @@ namespace libtorrent int piece_picker::unverified_blocks() const { int counter = 0; - for (int k = 0; k < num_download_categories; ++k) + for (int k = 0; k < piece_pos::num_download_categories; ++k) { for (std::vector::const_iterator i = m_downloads[k].begin(); i != m_downloads[k].end(); ++i) diff --git a/src/torrent.cpp b/src/torrent.cpp index 46d8bbafc..7c73f1c49 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -4435,14 +4435,16 @@ namespace libtorrent if (k->timed_out || k->not_wanted) continue; if (int(k->block.piece_index) != j->piece) continue; m_picker->mark_as_downloading(k->block, p->peer_info_struct() - , (piece_picker::piece_state_t)p->peer_speed()); + , (piece_picker::piece_state_t)p->peer_speed() + , p->picker_options()); } for (std::vector::const_iterator k = rq.begin() , end(rq.end()); k != end; ++k) { if (int(k->block.piece_index) != j->piece) continue; m_picker->mark_as_downloading(k->block, p->peer_info_struct() - , (piece_picker::piece_state_t)p->peer_speed()); + , (piece_picker::piece_state_t)p->peer_speed() + , p->picker_options()); } } } diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index 6546fe62e..12fc5d7b1 100644 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -625,7 +625,7 @@ void web_peer_connection::on_receive(error_code const& error // if the status code is not one of the accepted ones, abort if (!is_ok_status(m_parser.status_code())) { - // TODO: 3 just make this peer not have the pieces + // TODO: 2 just make this peer not have the pieces // associated with the file we just requested. Only // when it doesn't have any of the file do the following int retry_time = atoi(m_parser.header("retry-after").c_str()); diff --git a/test/test_piece_picker.cpp b/test/test_piece_picker.cpp index daf5e0073..82a7c2f69 100644 --- a/test/test_piece_picker.cpp +++ b/test/test_piece_picker.cpp @@ -257,14 +257,16 @@ std::vector pick_pieces(boost::shared_ptr const& p { std::vector picked; counters pc; - p->pick_pieces(string2vec(availability), picked, num_blocks, prefer_contiguous_blocks, peer_struct + p->pick_pieces(string2vec(availability), picked + , num_blocks, prefer_contiguous_blocks, peer_struct , state, options, suggested_pieces, 20, pc); print_pick(picked); TEST_CHECK(verify_pick(p, picked)); return picked; } -int test_pick(boost::shared_ptr const& p, int options = piece_picker::rarest_first) +int test_pick(boost::shared_ptr const& p + , int options = piece_picker::rarest_first) { const std::vector empty_vector; std::vector picked = pick_pieces(p, "*******", 1, 0, 0 @@ -360,14 +362,14 @@ int test_main() p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::fast); p->mark_as_finished(piece_block(0,1), 0); p->piece_info(0, st); - TEST_CHECK(st.requested == 1); - TEST_CHECK(st.finished == 1); - TEST_CHECK(st.state == piece_picker::fast); + TEST_EQUAL(st.requested, 1); + TEST_EQUAL(st.finished, 1); + TEST_EQUAL(st.state, piece_picker::fast); p->abort_download(piece_block(0,0), tmp_peer); p->piece_info(0, st); - TEST_CHECK(st.requested == 0); - TEST_CHECK(st.finished == 1); - TEST_CHECK(st.state == piece_picker::none); + TEST_EQUAL(st.requested, 0); + TEST_EQUAL(st.finished, 1); + TEST_EQUAL(st.state, piece_picker::none); picked = pick_pieces(p, "*******", blocks_per_piece, 0, 0, piece_picker::fast , options, empty_vector); TEST_CHECK(p->is_requested(piece_block(0, 0)) == false); @@ -437,9 +439,15 @@ int test_main() // make sure the block that is picked is from piece 5, since it // has the highest priority among the available pieces print_title("test pick highest priority"); - p = setup_picker("1111111", "* * * ", "1111121", ""); + p = setup_picker("1111111", " ", "1111121", ""); TEST_CHECK(test_pick(p) == 5); + p = setup_picker("1111111", " ", "1171121", ""); + TEST_CHECK(test_pick(p) == 2); + + p = setup_picker("1111111", " ", "1131521", ""); + TEST_CHECK(test_pick(p) == 4); + // ======================================================== print_title("test reverse rarest first"); @@ -450,9 +458,10 @@ int test_main() for (int i = 0; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(expected_common_pieces[i / blocks_per_piece], i % blocks_per_piece)); - // piece 3 should be prioritized since it's a partial + // piece 3 should NOT be prioritized since it's a partial, and not + // reversed. Reversed partials are considered reversed p = setup_picker("1122111", " ", "3333333", " 1 "); - TEST_CHECK(test_pick(p, piece_picker::rarest_first | piece_picker::reverse) == 3); + TEST_CHECK(test_pick(p, piece_picker::rarest_first | piece_picker::reverse) == 2); // ======================================================== @@ -462,14 +471,14 @@ int test_main() // it is not a whole piece print_title("test pick whole pieces"); p = setup_picker("2212222", " ", "1111111", "1023460"); - picked = pick_pieces(p, "****** ", 1, 1 * blocks_per_piece + picked = pick_pieces(p, "****** ", 1, blocks_per_piece , &peer_struct, piece_picker::fast, options, empty_vector); TEST_EQUAL(int(picked.size()), 3); for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i) TEST_EQUAL(picked[i].piece_index, 2); p = setup_picker("1111111", " ", "1111111", ""); - picked = pick_pieces(p, "****** ", 1, 1 * blocks_per_piece + picked = pick_pieces(p, "****** ", 1, blocks_per_piece , &peer_struct, piece_picker::fast, options, empty_vector); TEST_EQUAL(int(picked.size()), blocks_per_piece); for (int i = 0; i < blocks_per_piece && i < int(picked.size()); ++i) @@ -641,8 +650,9 @@ int test_main() picked = pick_pieces(p, "*******", 7 * blocks_per_piece, 0, 0, piece_picker::fast , piece_picker::sequential, empty_vector); - // the piece with priority 0 was not picked - TEST_CHECK(int(picked.size()) == 6 * blocks_per_piece); + // the piece with priority 0 was not picked, everything else should + // be picked + TEST_EQUAL(int(picked.size()), 6 * blocks_per_piece); // the first two pieces picked should be 3 and 5 since those have priority 7 for (int i = 0; i < 2 * blocks_per_piece; ++i) @@ -650,7 +660,7 @@ int test_main() int expected[] = {-1, -1, 0, 1, 2, 6}; for (int i = 2 * blocks_per_piece; i < int(picked.size()); ++i) - TEST_CHECK(picked[i].piece_index == expected[i / blocks_per_piece]); + TEST_EQUAL(picked[i].piece_index, expected[i / blocks_per_piece]); // ======================================================== @@ -782,7 +792,7 @@ int test_main() TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(picked.front().piece_index == 1); - p->set_piece_priority(0, 1); + p->set_piece_priority(0, 7); picked = pick_pieces(p, "*******", 1, 0, 0, piece_picker::fast, options, empty_vector); TEST_CHECK(int(picked.size()) >= 1); TEST_CHECK(picked.front().piece_index == 0); @@ -874,7 +884,7 @@ int test_main() // make sure we still pick from a partial piece even when prefering whole pieces picked.clear(); - p->pick_pieces(string2vec(" * "), picked, 1, 1 * blocks_per_piece, 0 + p->pick_pieces(string2vec(" * "), picked, 1, blocks_per_piece, 0 , piece_picker::fast, piece_picker::rarest_first | piece_picker::align_expanded_pieces, empty_vector, 20 , pc); @@ -1108,7 +1118,7 @@ int test_main() // are picked if there's no other choice p = setup_picker("1111111", " ", "", ""); p->mark_as_downloading(piece_block(2,2), &tmp1, piece_picker::fast); - picked = pick_pieces(p, "*******", 7 * blocks_per_piece - 1, 1 * blocks_per_piece + picked = pick_pieces(p, "*******", 7 * blocks_per_piece - 1, blocks_per_piece , 0, piece_picker::fast, options, empty_vector); TEST_CHECK(picked.size() == 7 * blocks_per_piece - 1); TEST_CHECK(std::find(picked.begin(), picked.end(), piece_block(2,2)) == picked.end()); @@ -1141,7 +1151,7 @@ int test_main() print_title("test parole mode"); p = setup_picker("3333133", " ", "", ""); p->mark_as_finished(piece_block(0, 0), 0); - picked = pick_pieces(p, "*******", 1, 1 * blocks_per_piece, 0 + picked = pick_pieces(p, "*******", 1, blocks_per_piece, 0 , piece_picker::fast , options | piece_picker::on_parole | piece_picker::prioritize_partials, empty_vector); TEST_EQUAL(int(picked.size()), blocks_per_piece - 1); @@ -1150,7 +1160,7 @@ int test_main() // make sure that the partial piece is not picked by a // peer that is has not downloaded/requested the other blocks - picked = pick_pieces(p, "*******", 1, 1 * blocks_per_piece + picked = pick_pieces(p, "*******", 1, blocks_per_piece , &peer_struct, piece_picker::fast , options | piece_picker::on_parole | piece_picker::prioritize_partials, empty_vector); TEST_EQUAL(int(picked.size()), blocks_per_piece); @@ -1165,7 +1175,7 @@ int test_main() int v[] = {1, 5}; std::vector suggested_pieces(v, v + 2); - picked = pick_pieces(p, "****************", 1, 1 * blocks_per_piece + picked = pick_pieces(p, "****************", 1, blocks_per_piece , 0, piece_picker::fast, options, suggested_pieces); TEST_CHECK(int(picked.size()) >= blocks_per_piece); for (int i = 1; i < int(picked.size()); ++i) @@ -1175,14 +1185,14 @@ int test_main() p->set_piece_priority(2, 0); p->set_piece_priority(3, 0); - picked = pick_pieces(p, "****************", 1, 1 * blocks_per_piece + picked = pick_pieces(p, "****************", 1, blocks_per_piece , 0, piece_picker::fast, options, suggested_pieces); TEST_CHECK(int(picked.size()) >= blocks_per_piece); for (int i = 1; i < int(picked.size()); ++i) TEST_CHECK(picked[i] == piece_block(5, i)); p = setup_picker("1111222233334444", "**** ", "", ""); - picked = pick_pieces(p, "****************", 1, 1 * blocks_per_piece + picked = pick_pieces(p, "****************", 1, blocks_per_piece , 0, piece_picker::fast, options, suggested_pieces); TEST_CHECK(int(picked.size()) >= blocks_per_piece); for (int i = 1; i < int(picked.size()); ++i) @@ -1195,13 +1205,13 @@ int test_main() // we have less than half of the pieces p = setup_picker("2122222211221222", " ", "", ""); // make sure it's not dirty - pick_pieces(p, "****************", 1, 1 * blocks_per_piece, 0); + pick_pieces(p, "****************", 1, blocks_per_piece, 0); print_availability(p); p->dec_refcount(string2vec("** ** ** * "), &tmp0); print_availability(p); TEST_CHECK(verify_availability(p, "1022112200220222")); // make sure it's not dirty - pick_pieces(p, "****************", 1, 1 * blocks_per_piece, 0); + pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->inc_refcount(string2vec(" ** ** * * "), &tmp8); print_availability(p); TEST_CHECK(verify_availability(p, "1132123201220322")); @@ -1213,25 +1223,25 @@ int test_main() p = setup_picker("0000000000000000", " ", "", ""); // make sure it's not dirty - pick_pieces(p, "****************", 1, 1 * blocks_per_piece, 0); + pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->inc_refcount_all(&tmp0); print_availability(p); TEST_CHECK(verify_availability(p, "1111111111111111")); // make sure it's not dirty - pick_pieces(p, "****************", 1, 1, 0); + pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->dec_refcount(string2vec(" **** ** "), &tmp0); print_availability(p); TEST_CHECK(verify_availability(p, "1100001100111111")); // make sure it's not dirty - pick_pieces(p, "****************", 1, 1, 0); + pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->inc_refcount(string2vec(" **** ** "), &tmp0); TEST_CHECK(verify_availability(p, "1111111111111111")); // make sure it's not dirty - pick_pieces(p, "****************", 1, 1, 0); + pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->dec_refcount_all(&tmp0); TEST_CHECK(verify_availability(p, "0000000000000000")); @@ -1240,13 +1250,64 @@ int test_main() TEST_CHECK(verify_availability(p, "1111111111111111")); // make sure it's not dirty - pick_pieces(p, "****************", 1, 1, 0); + pick_pieces(p, "****************", 1, blocks_per_piece, 0); p->dec_refcount(3, &tmp1); print_availability(p); TEST_CHECK(verify_availability(p, "1110111111111111")); // ======================================================== + // test reversed peers + print_title("test reversed peers"); + p = setup_picker("3333333", " *****", "", ""); + + // a reversed peer picked a block from piece 0 + // This should make the piece reversed + p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::slow + , piece_picker::reverse); + + TEST_EQUAL(test_pick(p, piece_picker::rarest_first), 1); + + // make sure another reversed peer pick the same piece + TEST_EQUAL(test_pick(p, piece_picker::rarest_first | piece_picker::reverse), 0); + +// ======================================================== + + // test reversed pieces upgrading to normal pieces + print_title("test reversed piece upgrade"); + + p = setup_picker("3333333", " *****", "", ""); + + // make piece 0 partial and reversed + p->mark_as_downloading(piece_block(0,1), &tmp1, piece_picker::slow + , piece_picker::reverse); + TEST_EQUAL(test_pick(p), 1); + + // now have a regular peer pick the reversed block. It should now + // have turned into a regular one and be prioritized + p->mark_as_downloading(piece_block(0,2), &tmp1, piece_picker::fast); + TEST_EQUAL(test_pick(p), 0); + + +// ======================================================== + + // test pieces downgrading to reversed pieces + print_title("test reversed piece downgrade"); +// now make sure a piece can be demoted to reversed if there are no +// other outstanding requests + + p = setup_picker("3333333", " ", "", ""); + + // make piece 0 partial and not reversed + p->mark_as_finished(piece_block(0,1), &tmp1); + + // a reversed peer picked a block from piece 0 + // This should make the piece reversed + p->mark_as_downloading(piece_block(0,0), &tmp1, piece_picker::slow + , piece_picker::reverse); + + TEST_EQUAL(test_pick(p, piece_picker::rarest_first | piece_picker::reverse), 0); + // MISSING TESTS: // 2. write_failed