diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index e493e1d8a2..761a297f82 100644 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -88,6 +88,20 @@ namespace libtorrent { bool last_piece; }; + // the piece picker tracks: + // 1. The blocks in which pieces we have sent requests for + // 2. Which peers we sent the requests to + // 3. The availability of each piece + // 4. The priority of each piece + // 5. Which blocks and pieces have been written to disk (versus being in the cache) + // 6. Which pieces have passed the hash check (i.e. we have them) + // 7. Cursors for sequential download + // 8. The number of pad-bytes in each piece + // All this in a data structure to make it cheap to pick the next piece to + // request from a peer. + // If we "have" a piece, it means it has passed the hash check. If a piece + // has been "flushed", it means it's been stored to disk. + // When saving resume data, we only care about "flushed" pieces. struct TORRENT_EXTRA_EXPORT piece_picker { // only defined when TORRENT_PICKER_LOG is defined, used for debugging @@ -247,11 +261,13 @@ namespace libtorrent { // seed void we_have_all(); - // This indicates that we just received this piece - // it means that the refcounter will indicate that + // A piece completes when it has passed the hash check *and* been + // completely written to disk. The piece picker no longer need to track + // the state of individual blocks + // The refcounter will indicate that // we are not interested in this piece anymore // (i.e. we don't have to maintain a refcount) - void we_have(piece_index_t); + void piece_flushed(piece_index_t); void we_dont_have(piece_index_t); // the lowest piece index we do not have @@ -264,8 +280,14 @@ namespace libtorrent { void resize(std::int64_t total_size, int piece_size); int num_pieces() const { return int(m_piece_map.size()); } + // returns true if we have the piece or if the piece + // has passed the hash check bool have_piece(piece_index_t) const; + // returns true if the piece has been completely downloaded and + // successfully flushed to disk (i.e. "finished"). + bool is_piece_flushed(piece_index_t) const; + bool is_downloading(piece_index_t const index) const { TORRENT_ASSERT(index >= piece_index_t(0)); @@ -403,10 +425,6 @@ namespace libtorrent { // only valid for v2 torrents bool is_hashing(piece_index_t piece) const; - // returns true if we have the piece or if the piece - // has passed the hash check - bool has_piece_passed(piece_index_t) const; - // returns the number of blocks there is in the given piece int blocks_in_piece(piece_index_t) const; @@ -457,7 +475,7 @@ namespace libtorrent { // number of pieces whose hash has passed (but haven't necessarily // been flushed to disk yet) - int num_passed() const { return m_num_passed; } + int num_have() const { return m_num_have; } // return true if all the pieces we want have passed the hash check (but // may not have been written to disk yet) @@ -471,11 +489,11 @@ namespace libtorrent { // finished. Note that any piece we *have* implies it's both passed the // hash check *and* been written to disk. // num_pieces() - m_num_filtered - m_num_have_filtered - // <= (num_passed() - m_num_have_filtered) + // <= (m_num_have - m_num_have_filtered) // this can be simplified. Note how m_num_have_filtered appears on both // side of the equation. // - return num_pieces() - m_num_filtered <= num_passed(); + return num_pieces() - m_num_filtered <= m_num_have; } bool is_seeding() const { return m_num_have == num_pieces(); } @@ -535,6 +553,9 @@ namespace libtorrent { int blocks_per_piece() const; int piece_size(piece_index_t p) const; + void account_have(piece_index_t); + void account_lost(piece_index_t); + piece_extent_t extent_for(piece_index_t) const; index_range extent_for(piece_extent_t) const; @@ -830,9 +851,6 @@ namespace libtorrent { // the availability counters of the pieces int m_seeds = 0; - // the number of pieces that have passed the hash check - int m_num_passed = 0; - // this vector contains all piece indices that are pickable // sorted by priority. Pieces are in random random order // among pieces with the same priority @@ -890,7 +908,7 @@ namespace libtorrent { // all the subsequent pieces piece_index_t m_reverse_cursor{0}; - // the number of pieces we have (i.e. passed + flushed). + // the number of pieces we have (i.e. passed hash check). // This includes pieces that we have filtered but still have int m_num_have = 0; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 0de5168bd0..226b6a92ca 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -856,6 +856,7 @@ namespace libtorrent { #endif // returns true if we have downloaded the given piece + // but not necessarily flushed it to disk bool have_piece(piece_index_t index) const { if (!valid_metadata()) return false; @@ -872,15 +873,6 @@ namespace libtorrent { return m_picker->have_piece(index); } - // returns true if we have downloaded the given piece - bool has_piece_passed(piece_index_t index) const - { - if (!valid_metadata()) return false; - if (index < piece_index_t(0) || index >= torrent_file().end_piece()) return false; - if (!has_picker()) return m_have_all; - return m_picker->has_piece_passed(index); - } - #ifndef TORRENT_DISABLE_PREDICTIVE_PIECES // a predictive piece is a piece that we might // not have yet, but still announced to peers, anticipating that @@ -903,6 +895,9 @@ namespace libtorrent { public: + // the number of pieces that have passed + // hash check, but aren't necessarily + // flushed to disk yet int num_have() const { // pretend we have every piece when in seed mode @@ -912,16 +907,6 @@ namespace libtorrent { return 0; } - // the number of pieces that have passed - // hash check, but aren't necessarily - // flushed to disk yet - int num_passed() const - { - if (has_picker()) return m_picker->num_passed(); - if (m_have_all) return m_torrent_file->num_pieces(); - return 0; - } - // when we get a have message, this is called for that piece void peer_has(piece_index_t index, peer_connection const* peer); diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index d019088ff0..d3a1278eaf 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -542,7 +542,7 @@ namespace libtorrent { { if (m_have_piece[j] && t->piece_priority(j) > dont_download - && !p.has_piece_passed(j)) + && !p.have_piece(j)) { interested = true; #ifndef TORRENT_DISABLE_LOGGING @@ -2084,7 +2084,7 @@ namespace libtorrent { // it's important to update whether we're interested in this peer before // calling disconnect_if_redundant, otherwise we may disconnect even if // we are interested - if (!t->has_piece_passed(index) + if (!t->have_piece(index) && !t->is_upload_only() && !is_interesting() && (!t->has_picker() || t->picker().piece_priority(index) != dont_download)) @@ -2418,7 +2418,7 @@ namespace libtorrent { , valid_piece_index ? t->torrent_file().piece_size(r.piece) : -1 , t->torrent_file().num_pieces() - , valid_piece_index ? t->has_piece_passed(r.piece) : 0 + , valid_piece_index ? t->have_piece(r.piece) : 0 , static_cast(m_superseed_piece[0]) , static_cast(m_superseed_piece[1])); } @@ -2433,7 +2433,7 @@ namespace libtorrent { bool const peer_interested = bool(m_peer_interested); t->alerts().emplace_alert( t->get_handle(), m_remote, m_peer_id, r - , t->has_piece_passed(r.piece), peer_interested, true); + , t->user_have_piece(r.piece), peer_interested, true); } return; } @@ -2502,7 +2502,7 @@ namespace libtorrent { { t->alerts().emplace_alert( t->get_handle(), m_remote, m_peer_id, r - , t->has_piece_passed(r.piece) + , t->user_have_piece(r.piece) , false, false); } @@ -2515,7 +2515,7 @@ namespace libtorrent { // is not choked if (r.piece < piece_index_t(0) || r.piece >= t->torrent_file().end_piece() - || (!t->has_piece_passed(r.piece) + || (!t->user_have_piece(r.piece) #ifndef TORRENT_DISABLE_PREDICTIVE_PIECES && !t->is_predictive_piece(r.piece) #endif @@ -2537,7 +2537,7 @@ namespace libtorrent { , valid_piece_index ? t->torrent_file().piece_size(r.piece) : -1 , ti.num_pieces() - , t->has_piece_passed(r.piece) + , t->user_have_piece(r.piece) , t->block_size()); } #endif @@ -2553,7 +2553,7 @@ namespace libtorrent { bool const peer_interested = bool(m_peer_interested); t->alerts().emplace_alert( t->get_handle(), m_remote, m_peer_id, r - , t->has_piece_passed(r.piece), peer_interested, false); + , t->user_have_piece(r.piece), peer_interested, false); } // every ten invalid request, remind the peer that it's choked @@ -3516,7 +3516,7 @@ namespace libtorrent { // to download it, request it if (index < m_have_piece.end_index() && m_have_piece[index] - && !t->has_piece_passed(index) + && !t->have_piece(index) && t->valid_metadata() && t->has_picker() && t->picker().piece_priority(index) > dont_download) @@ -4001,7 +4001,7 @@ namespace libtorrent { { std::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); - TORRENT_ASSERT(t->has_piece_passed(piece)); + TORRENT_ASSERT(t->have_piece(piece)); TORRENT_ASSERT(piece < t->torrent_file().end_piece()); } #endif @@ -5354,7 +5354,7 @@ namespace libtorrent { continue; } - if (!t->has_piece_passed(r.piece) && !seed_mode) + if (!t->have_piece(r.piece) && !seed_mode) { #ifndef TORRENT_DISABLE_PREDICTIVE_PIECES // we don't have this piece yet, but we anticipate to have diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index fb8819db43..e7e10040f3 100644 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -197,7 +197,6 @@ namespace libtorrent { m_have_pad_bytes = 0; m_filtered_pad_bytes += m_have_filtered_pad_bytes; m_have_filtered_pad_bytes = 0; - m_num_passed = 0; m_dirty = true; for (auto& m : m_piece_map) { @@ -633,31 +632,42 @@ namespace libtorrent { { piece_pos const& p = *i; + int const pad_bytes = pad_bytes_in_piece(piece); if (p.filtered()) { - if (p.index != piece_pos::we_have_index) + if (p.downloading()) { - ++num_filtered; - num_filtered_pad_bytes += pad_bytes_in_piece(piece); + auto dl = find_dl_piece(p.download_queue(), piece); + if (dl->passed_hash_check) + { + ++num_have_filtered; + num_have_filtered_pad_bytes += pad_bytes; + } + else + { + ++num_filtered; + num_filtered_pad_bytes += pad_bytes; + } } - else + else if (p.have()) { ++num_have_filtered; - num_have_filtered_pad_bytes += pad_bytes_in_piece(piece); + num_have_filtered_pad_bytes += pad_bytes; + } + else + { + ++num_filtered; + num_filtered_pad_bytes += pad_bytes; } } #ifdef TORRENT_DEBUG_REFCOUNTS TORRENT_ASSERT(int(p.have_peers.size()) == p.peer_count + m_seeds); #endif - if (p.index == piece_pos::we_have_index) + if (p.have()) { ++num_have; - num_have_pad_bytes += pad_bytes_in_piece(piece); - } - - if (p.index == piece_pos::we_have_index) - { + num_have_pad_bytes += pad_bytes; TORRENT_ASSERT(t == nullptr || t->have_piece(piece)); TORRENT_ASSERT(p.downloading() == false); } @@ -669,6 +679,12 @@ namespace libtorrent { if (p.downloading()) { + auto dl = find_dl_piece(p.download_queue(), piece); + if (dl->passed_hash_check) + { + ++num_have; + num_have_pad_bytes += pad_bytes; + } if (p.reverse()) TORRENT_ASSERT(prio == -1 || (prio % piece_picker::prio_factor == 2)); else @@ -1615,11 +1631,12 @@ namespace libtorrent { TORRENT_ASSERT(!i->passed_hash_check); i->passed_hash_check = true; - ++m_num_passed; + + account_have(index); if (i->finished < blocks_in_piece(index)) return; - we_have(index); + piece_flushed(index); } void piece_picker::we_dont_have(piece_index_t const index) @@ -1632,7 +1649,8 @@ namespace libtorrent { << index << ")" << std::endl; #endif - if (!p.have()) + bool have_piece = p.have(); + if (!have_piece) { // even though we don't have the piece, it // might still have passed hash check @@ -1640,29 +1658,14 @@ namespace libtorrent { if (download_state == piece_pos::piece_open) return; auto const i = find_dl_piece(download_state, index); - if (i->passed_hash_check) - { - i->passed_hash_check = false; - TORRENT_ASSERT(m_num_passed > 0); - --m_num_passed; - } + have_piece = i->passed_hash_check; erase_download_piece(i); - return; } - TORRENT_ASSERT(m_num_passed > 0); - --m_num_passed; - if (p.filtered()) - { - m_filtered_pad_bytes += pad_bytes_in_piece(index); - ++m_num_filtered; + if (have_piece) + account_lost(index); - TORRENT_ASSERT(m_have_filtered_pad_bytes >= pad_bytes_in_piece(index)); - m_have_filtered_pad_bytes -= pad_bytes_in_piece(index); - TORRENT_ASSERT(m_num_have_filtered > 0); - --m_num_have_filtered; - } - else + if (!p.filtered()) { // update cursors if (index < m_cursor) m_cursor = index; @@ -1674,9 +1677,6 @@ namespace libtorrent { } } - --m_num_have; - m_have_pad_bytes -= pad_bytes_in_piece(index); - TORRENT_ASSERT(m_have_pad_bytes >= 0); p.set_not_have(); if (m_dirty) return; @@ -1687,13 +1687,11 @@ namespace libtorrent { // downloaded a piece, and that no further attempts // to pick that piece should be made. The piece will // be removed from the available piece list. - void piece_picker::we_have(piece_index_t const index) + void piece_picker::piece_flushed(piece_index_t const index) { -#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS INVARIANT_CHECK; -#endif #ifdef TORRENT_PICKER_LOG - std::cerr << "[" << this << "] " << "piece_picker::we_have(" + std::cerr << "[" << this << "] " << "piece_picker::piece_flushed(" << index << ")" << std::endl; #endif piece_pos& p = m_piece_map[index]; @@ -1704,31 +1702,27 @@ namespace libtorrent { if (p.have()) return; auto const state = p.download_queue(); - if (state != piece_pos::piece_open) + bool passed_hash_check = false; + if (p.downloading()) { auto const i = find_dl_piece(state, index); TORRENT_ASSERT(i != m_downloads[state].end()); TORRENT_ASSERT(i->hashing == 0); - // decrement num_passed here to compensate - // for the unconditional increment further down - if (i->passed_hash_check) --m_num_passed; + passed_hash_check = i->passed_hash_check; + TORRENT_ASSERT(i->locked == false); + if (i->locked) return; erase_download_piece(i); } - if (p.filtered()) + if (!passed_hash_check) { - TORRENT_ASSERT(m_filtered_pad_bytes >= pad_bytes_in_piece(index)); - m_filtered_pad_bytes -= pad_bytes_in_piece(index); - TORRENT_ASSERT(m_num_filtered > 0); - --m_num_filtered; - - m_have_filtered_pad_bytes += pad_bytes_in_piece(index); - ++m_num_have_filtered; + // if we go straight from open to flushed, we need to make sure we + // maintain the accounting as-if we had downloaded it and checked + // the hash first. e.g. when we load resume data, we set up the + // piece states to indicate they're already on disk + account_have(index); } - ++m_num_have; - ++m_num_passed; - m_have_pad_bytes += pad_bytes_in_piece(index); - TORRENT_ASSERT(m_have_pad_bytes <= num_pad_bytes()); + p.set_have(); if (m_cursor == prev(m_reverse_cursor) && m_cursor == index) @@ -1783,7 +1777,6 @@ namespace libtorrent { m_filtered_pad_bytes = 0; m_cursor = m_piece_map.end_index(); m_reverse_cursor = piece_index_t{0}; - m_num_passed = num_pieces(); m_num_have = num_pieces(); for (auto& queue : m_downloads) queue.clear(); @@ -2538,12 +2531,13 @@ namespace { return ret; } - // have piece means that the piece passed hash check - // AND has been successfully written to disk - bool piece_picker::have_piece(piece_index_t const index) const + bool piece_picker::is_piece_flushed(piece_index_t const index) const { + TORRENT_ASSERT(index < m_piece_map.end_index()); + TORRENT_ASSERT(index >= piece_index_t(0)); + piece_pos const& p = m_piece_map[index]; - return p.index == piece_pos::we_have_index; + return p.have(); } int piece_picker::blocks_in_piece(piece_index_t const index) const @@ -2912,13 +2906,13 @@ namespace { return i->hashing > 0; } - bool piece_picker::has_piece_passed(piece_index_t const index) const + bool piece_picker::have_piece(piece_index_t const index) const { TORRENT_ASSERT(index < m_piece_map.end_index()); TORRENT_ASSERT(index >= piece_index_t(0)); piece_pos const& p = m_piece_map[index]; - if (p.index == piece_pos::we_have_index) return true; + if (p.have()) return true; auto const state = p.download_queue(); if (state == piece_pos::piece_open) @@ -3474,8 +3468,7 @@ namespace { // but it seems reasonable to not break the // accounting over it. i->passed_hash_check = false; - TORRENT_ASSERT(m_num_passed > 0); - --m_num_passed; + account_lost(piece); } // prevent this piece from being picked until it's restored @@ -3483,7 +3476,7 @@ namespace { } // TODO: 2 it would be nice if this could be folded into lock_piece() - // the main distinction is that this also maintains the m_num_passed + // the main distinction is that this also maintains the m_num_have // counter and the passed_hash_check member // Is there ever a case where we call write failed without also locking // the piece? Perhaps write_failed() should imply locking it. @@ -3526,8 +3519,7 @@ namespace { // some of the blocks to disk, which means we // can't consider the piece complete i->passed_hash_check = false; - TORRENT_ASSERT(m_num_passed > 0); - --m_num_passed; + account_lost(block.piece_index); } // prevent this hash job from actually completing @@ -3697,7 +3689,7 @@ namespace { return; if (i->passed_hash_check && i->hashing == 0) - we_have(i->index); + piece_flushed(i->index); } #if TORRENT_USE_INVARIANT_CHECKS @@ -3715,6 +3707,8 @@ namespace { TORRENT_ASSERT(bytes <= piece_size(piece)); m_num_pad_bytes += bytes; + // We don't support *changing* the number of pad bytes in a piece + TORRENT_ASSERT(m_pads_in_piece.count(piece) == 0); m_pads_in_piece[piece] = bytes; piece_pos& p = m_piece_map[piece]; @@ -3734,7 +3728,7 @@ namespace { if (piece_size(piece) == bytes) { // the entire piece is a pad file - we_have(piece); + piece_flushed(piece); } } @@ -3931,4 +3925,44 @@ namespace { return ret; } + void piece_picker::account_have(piece_index_t const index) + { + ++m_num_have; + piece_pos& p = m_piece_map[index]; + TORRENT_ASSERT(!p.have()); + int const pad_bytes = pad_bytes_in_piece(index); + if (p.filtered()) + { + TORRENT_ASSERT(m_filtered_pad_bytes >= pad_bytes); + m_filtered_pad_bytes -= pad_bytes; + TORRENT_ASSERT(m_num_filtered > 0); + --m_num_filtered; + + m_have_filtered_pad_bytes += pad_bytes; + ++m_num_have_filtered; + } + m_have_pad_bytes += pad_bytes; + TORRENT_ASSERT(m_have_pad_bytes <= num_pad_bytes()); + } + + void piece_picker::account_lost(piece_index_t const index) + { + TORRENT_ASSERT(m_num_have > 0); + --m_num_have; + piece_pos& p = m_piece_map[index]; + int const pad_bytes = pad_bytes_in_piece(index); + if (p.filtered()) + { + m_filtered_pad_bytes += pad_bytes; + TORRENT_ASSERT(m_filtered_pad_bytes <= num_pad_bytes()); + ++m_num_filtered; + + TORRENT_ASSERT(m_have_filtered_pad_bytes >= pad_bytes); + m_have_filtered_pad_bytes -= pad_bytes; + TORRENT_ASSERT(m_num_have_filtered > 0); + --m_num_have_filtered; + } + TORRENT_ASSERT(m_have_pad_bytes >= pad_bytes); + m_have_pad_bytes -= pad_bytes; + } } diff --git a/src/torrent.cpp b/src/torrent.cpp index af5487214e..d47a5ab94b 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -755,7 +755,7 @@ bool is_downloading_state(int const st) { ec.assign(errors::no_metadata, libtorrent_category()); } - else if (!has_piece_passed(piece)) + else if (!user_have_piece(piece)) { ec.assign(errors::invalid_piece_index, libtorrent_category()); } @@ -1298,14 +1298,18 @@ bool is_downloading_state(int const st) piece_refcount(piece_refcount const&) = delete; piece_refcount& operator=(piece_refcount const&) = delete; + void disarm() { m_armed = false; } + ~piece_refcount() { - m_picker.dec_refcount(m_piece, nullptr); + if (m_armed) + m_picker.dec_refcount(m_piece, nullptr); } private: piece_picker& m_picker; piece_index_t m_piece; + bool m_armed = true; }; void torrent::add_piece_async(piece_index_t const piece @@ -1413,6 +1417,10 @@ bool is_downloading_state(int const st) } } m_ses.deferred_submit_jobs(); + // if we don't have a picker anymore, we don't need to (and shouldn't) + // decrement the refcount + if (!m_picker) + refcount.disarm(); } void torrent::on_disk_write_complete(storage_error const& error @@ -2210,7 +2218,7 @@ bool is_downloading_state(int const st) { if (!m_add_torrent_params->have_pieces[i]) continue; need_picker(); - m_picker->we_have(i); + m_picker->piece_flushed(i); inc_stats_counter(counters::num_piece_passed); update_gauge(); we_have(i, true); @@ -2230,7 +2238,7 @@ bool is_downloading_state(int const st) continue; } - if (has_piece_passed(piece)) + if (have_piece(piece)) continue; // being in seed mode and missing a piece is not compatible. @@ -2557,7 +2565,7 @@ bool is_downloading_state(int const st) if (has_picker() || !m_have_all) { need_picker(); - m_picker->we_have(piece); + m_picker->piece_flushed(piece); set_need_save_resume(torrent_handle::if_download_progress); update_gauge(); } @@ -3915,17 +3923,17 @@ namespace { if (m_seed_mode) return std::int64_t(0); if (!has_picker()) return is_seed() ? std::int64_t(0) : m_torrent_file->total_size(); + piece_count const pc = m_picker->have(); std::int64_t left = m_torrent_file->total_size() - - std::int64_t(m_picker->num_passed()) * m_torrent_file->piece_length(); + - std::int64_t(pc.num_pieces) * m_torrent_file->piece_length(); // if we have the last piece, we may have subtracted too much, as it can // be smaller than the normal piece size. // we have to correct it - piece_index_t const last_piece = prev(m_torrent_file->end_piece()); - if (m_picker->has_piece_passed(last_piece)) + if (pc.last_piece) { - left += m_torrent_file->piece_length() - m_torrent_file->piece_size(last_piece); + left += m_torrent_file->piece_length() - m_torrent_file->piece_size(m_torrent_file->last_piece()); } return left; @@ -4166,7 +4174,7 @@ namespace { void torrent::we_have(piece_index_t const index, bool const loading_resume) { TORRENT_ASSERT(is_single_thread()); - TORRENT_ASSERT(!has_picker() || m_picker->has_piece_passed(index)); + TORRENT_ASSERT(!has_picker() || m_picker->have_piece(index)); inc_stats_counter(counters::num_have_pieces); @@ -4348,11 +4356,11 @@ namespace { if (!has_picker() || verified_piece == piece || !m_picker->is_piece_finished(verified_piece) - || m_picker->has_piece_passed(verified_piece)) + || m_picker->have_piece(verified_piece)) continue; TORRENT_ASSERT(get_hash_picker().piece_verified(verified_piece)); - m_picker->we_have(verified_piece); + m_picker->piece_flushed(verified_piece); update_gauge(); we_have(verified_piece); } @@ -4401,11 +4409,11 @@ namespace { { // INVARIANT_CHECK; TORRENT_ASSERT(is_single_thread()); - TORRENT_ASSERT(!m_picker->has_piece_passed(index)); + TORRENT_ASSERT(!m_picker->have_piece(index)); #ifndef TORRENT_DISABLE_LOGGING if (should_log()) - debug_log("PIECE_PASSED (%d)", int(index)); + debug_log("PIECE_PASSED (%d) (num_have: %d)", int(index), num_have()); #endif // std::fprintf(stderr, "torrent::piece_passed piece:%d\n", index); @@ -4465,6 +4473,10 @@ namespace { update_gauge(); we_have(index); +#ifndef TORRENT_DISABLE_LOGGING + if (should_log()) + debug_log("we_have(%d) (num_have: %d)", int(index), num_have()); +#endif #ifndef TORRENT_DISABLE_STREAMING remove_time_critical_piece(index, true); #endif @@ -5138,7 +5150,7 @@ namespace { // if we already have the piece, no need to set the deadline. // however, if the user asked to get the piece data back, we still // need to read it and post it back to the user - if (is_seed() || (has_picker() && m_picker->has_piece_passed(piece))) + if (is_seed() || (has_picker() && m_picker->have_piece(piece))) { if (flags & torrent_handle::alert_when_available) read_piece(piece); @@ -7030,8 +7042,7 @@ namespace { auto const info = m_picker->blocks_for_piece(dp); for (int i = 0; i < int(info.size()); ++i) { - if (info[i].state == piece_picker::block_info::state_finished - || info[i].state == piece_picker::block_info::state_writing) + if (info[i].state == piece_picker::block_info::state_finished) bitmask.set_bit(i); } ret.unfinished_pieces.emplace(dp.index, std::move(bitmask)); @@ -7086,7 +7097,7 @@ namespace { { ret.have_pieces.resize(static_cast(max_piece), false); for (auto const i : ret.have_pieces.range()) - if (m_picker->have_piece(i)) ret.have_pieces.set_bit(i); + if (m_picker->is_piece_flushed(i)) ret.have_pieces.set_bit(i); } if (m_seed_mode) @@ -8647,7 +8658,7 @@ namespace { if (!valid_metadata()) return false; if (m_seed_mode) return true; if (m_have_all) return true; - if (m_picker && m_picker->num_passed() == m_picker->num_pieces()) return true; + if (m_picker && m_picker->is_seeding()) return true; return m_state == torrent_status::seeding; } @@ -11510,7 +11521,7 @@ namespace { file_storage const& fs = m_torrent_file->files(); for (auto const& dp : q) { - if (has_piece_passed(dp.index)) + if (have_piece(dp.index)) { // in this case this piece has already been accounted for in fp continue; @@ -12013,7 +12024,7 @@ namespace { { st->pieces.resize(num_pieces, false); for (auto const i : st->pieces.range()) - if (m_picker->has_piece_passed(i)) st->pieces.set_bit(i); + if (m_picker->have_piece(i)) st->pieces.set_bit(i); } else if (m_have_all) { @@ -12024,16 +12035,20 @@ namespace { st->pieces.resize(num_pieces, false); } } - st->num_pieces = num_passed(); + st->num_pieces = num_have(); #if TORRENT_USE_INVARIANT_CHECKS { // The documentation states that `num_pieces` is the count of number // of bits set in `pieces`. Ensure that invariant holds. int num_have_pieces = 0; - if (has_picker()) + if (m_seed_mode) + { + num_have_pieces = m_torrent_file->num_pieces(); + } + else if (has_picker()) { for (auto const i : m_torrent_file->piece_range()) - if (m_picker->has_piece_passed(i)) ++num_have_pieces; + if (m_picker->have_piece(i)) ++num_have_pieces; } else if (m_have_all) { diff --git a/test/test_file_progress.cpp b/test/test_file_progress.cpp index b8f8821eaa..6176455d06 100644 --- a/test/test_file_progress.cpp +++ b/test/test_file_progress.cpp @@ -58,7 +58,7 @@ TORRENT_TEST(init) for (auto const idx : fs.piece_range()) { piece_picker picker(fs.total_size(), fs.piece_length()); - picker.we_have(idx); + picker.piece_flushed(idx); aux::file_progress fp; fp.init(picker, fs); @@ -90,7 +90,7 @@ TORRENT_TEST(init2) for (auto const idx : fs.piece_range()) { piece_picker picker(fs.total_size(), fs.piece_length()); - picker.we_have(idx); + picker.piece_flushed(idx); aux::vector vec; aux::file_progress fp; diff --git a/test/test_piece_picker.cpp b/test/test_piece_picker.cpp index 35c3487ab1..ddee1db746 100644 --- a/test/test_piece_picker.cpp +++ b/test/test_piece_picker.cpp @@ -197,7 +197,7 @@ std::shared_ptr setup_picker( for (auto i = 0_piece; i < piece_index_t(num_pieces); ++i) { if (!have[i]) continue; - p->we_have(i); + p->piece_flushed(i); for (int j = 0; j < blocks_per_piece; ++j) TEST_CHECK(p->is_finished(piece_block(i, j))); } @@ -579,12 +579,12 @@ TORRENT_TEST(we_dont_have) { // make sure we_dont_have works auto p = setup_picker("1111111", "*******", "0100000", ""); - TEST_CHECK(p->have_piece(1_piece)); - TEST_CHECK(p->have_piece(2_piece)); + TEST_CHECK(p->is_piece_flushed(1_piece)); + TEST_CHECK(p->is_piece_flushed(2_piece)); p->we_dont_have(1_piece); p->we_dont_have(2_piece); - TEST_CHECK(!p->have_piece(1_piece)); - TEST_CHECK(!p->have_piece(2_piece)); + TEST_CHECK(!p->is_piece_flushed(1_piece)); + TEST_CHECK(!p->is_piece_flushed(2_piece)); auto picked = pick_pieces(p, "*** ** ", 1, 0, nullptr, options, empty_vector); TEST_CHECK(int(picked.size()) > 0); TEST_CHECK(picked.front().piece_index == 1_piece); @@ -639,7 +639,7 @@ TORRENT_TEST(resize) TEST_EQUAL(p->have().num_pieces, 0); TEST_EQUAL(p->have().pad_bytes, 0); - p->we_have(0_piece); + p->piece_flushed(0_piece); TEST_EQUAL(p->want().num_pieces, 6); TEST_EQUAL(p->want().pad_bytes, 20); @@ -855,13 +855,13 @@ TORRENT_TEST(priority_sequential_download) TORRENT_TEST(cursors_sweep_up_we_have) { - // sweep up, we_have() + // sweep up, piece_flushed() auto p = setup_picker("7654321", " ", "", ""); for (auto i = 0_piece; i < 7_piece; ++i) { TEST_EQUAL(p->cursor(), i); TEST_EQUAL(p->reverse_cursor(), 7_piece); - p->we_have(i); + p->piece_flushed(i); } TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); @@ -887,13 +887,13 @@ TORRENT_TEST(cursors_sweep_up_set_piece_priority) TORRENT_TEST(cursors_sweep_down_we_have) { - // sweep down, we_have() + // sweep down, piece_flushed() auto p = setup_picker("7654321", " ", "", ""); for (auto i = 6_piece; i >= 0_piece; --i) { TEST_EQUAL(p->cursor(), 0_piece); TEST_EQUAL(p->reverse_cursor(), next(i)); - p->we_have(i); + p->piece_flushed(i); } TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); @@ -937,15 +937,15 @@ TORRENT_TEST(cursors_sweep_in_set_priority) TORRENT_TEST(cursors_sweep_in_we_have) { - // sweep in, we_have() + // sweep in, piece_flushed() auto p = setup_picker("7654321", " ", "", ""); for (piece_index_t left(0), right(6); left <= 3_piece && right >= 3_piece; ++left, --right) { TEST_EQUAL(p->cursor(), left); TEST_EQUAL(p->reverse_cursor(), next(right)); - p->we_have(left); - p->we_have(right); + p->piece_flushed(left); + p->piece_flushed(right); } TEST_CHECK(p->is_finished()); TEST_CHECK(p->is_seeding()); @@ -1019,21 +1019,21 @@ TORRENT_TEST(cursors) auto p = setup_picker("7654321", " ", "", ""); TEST_EQUAL(p->cursor(), 0_piece); TEST_EQUAL(p->reverse_cursor(), 7_piece); - p->we_have(1_piece); + p->piece_flushed(1_piece); TEST_EQUAL(p->cursor(), 0_piece); TEST_EQUAL(p->reverse_cursor(), 7_piece); - p->we_have(0_piece); + p->piece_flushed(0_piece); TEST_EQUAL(p->cursor(), 2_piece); TEST_EQUAL(p->reverse_cursor(), 7_piece); - p->we_have(5_piece); + p->piece_flushed(5_piece); TEST_EQUAL(p->cursor(), 2_piece); TEST_EQUAL(p->reverse_cursor(), 7_piece); - p->we_have(6_piece); + p->piece_flushed(6_piece); TEST_EQUAL(p->cursor(), 2_piece); TEST_EQUAL(p->reverse_cursor(), 5_piece); - p->we_have(4_piece); - p->we_have(3_piece); - p->we_have(2_piece); + p->piece_flushed(4_piece); + p->piece_flushed(3_piece); + p->piece_flushed(2_piece); TEST_EQUAL(p->cursor(), 7_piece); TEST_EQUAL(p->reverse_cursor(), 0_piece); @@ -1072,7 +1072,7 @@ TORRENT_TEST(piece_priorities) TEST_EQUAL(p->want().num_pieces, 6); TEST_EQUAL(p->have_want().num_pieces, 0); p->mark_as_finished({0_piece, 0}, nullptr); - p->we_have(0_piece); + p->piece_flushed(0_piece); TEST_EQUAL(p->want().num_pieces, 6); TEST_EQUAL(p->have_want().num_pieces, 0); TEST_EQUAL(p->have().num_pieces, 1); @@ -1088,7 +1088,7 @@ TORRENT_TEST(piece_priorities) TEST_CHECK(picked[std::size_t(i)] == piece_block(piece_index_t(i / blocks_per_piece), i % blocks_per_piece)); // test changing priority on a piece we have - p->we_have(0_piece); + p->piece_flushed(0_piece); p->set_piece_priority(0_piece, dont_download); p->set_piece_priority(0_piece, low_priority); p->set_piece_priority(0_piece, dont_download); @@ -1238,7 +1238,7 @@ TORRENT_TEST(random_pick) for (int i = 0; i < 7; ++i) { piece_index_t const piece = test_pick(p, {}); - p->we_have(piece); + p->piece_flushed(piece); random_pieces.insert(piece); } TEST_CHECK(random_pieces.size() == 7); @@ -1694,49 +1694,50 @@ TORRENT_TEST(piece_passed) { auto p = setup_picker("1111111", "* ", "", "0300000"); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); - TEST_EQUAL(p->num_passed(), 1); + TEST_EQUAL(p->have_piece(0_piece), true); + TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->num_have(), 1); TEST_EQUAL(p->have().num_pieces, 1); p->piece_passed(1_piece); - TEST_EQUAL(p->num_passed(), 2); - TEST_EQUAL(p->have().num_pieces, 1); + TEST_EQUAL(p->num_have(), 2); + TEST_EQUAL(p->have().num_pieces, 2); - p->we_have(1_piece); + p->piece_flushed(1_piece); TEST_EQUAL(p->have().num_pieces, 2); p->mark_as_finished({2_piece, 0}, &tmp1); p->piece_passed(2_piece); - TEST_EQUAL(p->num_passed(), 3); - // just because the hash check passed doesn't mean - // we "have" the piece. We need to write it to disk first - TEST_EQUAL(p->have().num_pieces, 2); + TEST_EQUAL(p->num_have(), 3); + // since the hash check passed we "have" the piece. + TEST_EQUAL(p->have().num_pieces, 3); + TEST_EQUAL(p->is_piece_flushed(0_piece), true); + TEST_EQUAL(p->is_piece_flushed(1_piece), true); + TEST_EQUAL(p->is_piece_flushed(2_piece), false); // piece 2 already passed the hash check, as soon as we've // written all the blocks to disk, we should have that piece too p->mark_as_finished({2_piece, 1}, &tmp1); p->mark_as_finished({2_piece, 2}, &tmp1); p->mark_as_finished({2_piece, 3}, &tmp1); - TEST_EQUAL(p->have().num_pieces, 3); - TEST_EQUAL(p->have_piece(2_piece), true); + TEST_EQUAL(p->is_piece_flushed(2_piece), true); } TORRENT_TEST(piece_passed_causing_we_have) { auto p = setup_picker("1111111", "* ", "", "0700000"); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); - TEST_EQUAL(p->num_passed(), 1); + TEST_EQUAL(p->have_piece(0_piece), true); + TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->num_have(), 1); TEST_EQUAL(p->have().num_pieces, 1); p->mark_as_finished({1_piece, 3}, &tmp1); - TEST_EQUAL(p->num_passed(), 1); + TEST_EQUAL(p->num_have(), 1); TEST_EQUAL(p->have().num_pieces, 1); p->piece_passed(1_piece); - TEST_EQUAL(p->num_passed(), 2); + TEST_EQUAL(p->num_have(), 2); TEST_EQUAL(p->have().num_pieces, 2); } @@ -1760,38 +1761,38 @@ TORRENT_TEST(break_one_seed) TORRENT_TEST(we_dont_have2) { auto p = setup_picker("1111111", "* * ", "1101111", ""); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); - TEST_EQUAL(p->has_piece_passed(2_piece), true); - TEST_EQUAL(p->num_passed(), 2); + TEST_EQUAL(p->have_piece(0_piece), true); + TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->have_piece(2_piece), true); + TEST_EQUAL(p->num_have(), 2); TEST_EQUAL(p->have().num_pieces, 2); TEST_EQUAL(p->have_want().num_pieces, 1); TEST_EQUAL(p->want().num_pieces, 6); p->we_dont_have(0_piece); - TEST_EQUAL(p->has_piece_passed(0_piece), false); - TEST_EQUAL(p->has_piece_passed(1_piece), false); - TEST_EQUAL(p->has_piece_passed(2_piece), true); - TEST_EQUAL(p->num_passed(), 1); + TEST_EQUAL(p->have_piece(0_piece), false); + TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->have_piece(2_piece), true); + TEST_EQUAL(p->num_have(), 1); TEST_EQUAL(p->have().num_pieces, 1); TEST_EQUAL(p->have_want().num_pieces, 0); p = setup_picker("1111111", "* * ", "1101111", ""); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); - TEST_EQUAL(p->has_piece_passed(2_piece), true); - TEST_EQUAL(p->num_passed(), 2); + TEST_EQUAL(p->have_piece(0_piece), true); + TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->have_piece(2_piece), true); + TEST_EQUAL(p->num_have(), 2); TEST_EQUAL(p->have().num_pieces, 2); TEST_EQUAL(p->have_want().num_pieces, 1); TEST_EQUAL(p->want().num_pieces, 6); p->we_dont_have(2_piece); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); - TEST_EQUAL(p->has_piece_passed(2_piece), false); - TEST_EQUAL(p->num_passed(), 1); + TEST_EQUAL(p->have_piece(0_piece), true); + TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->have_piece(2_piece), false); + TEST_EQUAL(p->num_have(), 1); TEST_EQUAL(p->have().num_pieces, 1); TEST_EQUAL(p->have_want().num_pieces, 1); TEST_EQUAL(p->want().num_pieces, 6); @@ -1801,44 +1802,44 @@ TORRENT_TEST(dont_have_but_passed_hash_check) { auto p = setup_picker("1111111", "* * ", "1101111", "0200000"); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); TEST_EQUAL(p->have_piece(0_piece), true); TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->is_piece_flushed(0_piece), true); + TEST_EQUAL(p->is_piece_flushed(1_piece), false); p->piece_passed(1_piece); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), true); - TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->have_piece(0_piece), true); + TEST_EQUAL(p->have_piece(1_piece), true); + TEST_EQUAL(p->is_piece_flushed(1_piece), false); p->we_dont_have(1_piece); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); + TEST_EQUAL(p->have_piece(0_piece), true); TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->is_piece_flushed(1_piece), false); } TORRENT_TEST(write_failed) { auto p = setup_picker("1111111", "* * ", "1101111", "0200000"); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); + TEST_EQUAL(p->have_piece(0_piece), true); TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->is_piece_flushed(1_piece), false); p->piece_passed(1_piece); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), true); - TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->have_piece(0_piece), true); + TEST_EQUAL(p->have_piece(1_piece), true); + TEST_EQUAL(p->is_piece_flushed(1_piece), false); p->mark_as_writing({1_piece, 0}, &tmp1); p->write_failed({1_piece, 0}); - TEST_EQUAL(p->has_piece_passed(0_piece), true); - TEST_EQUAL(p->has_piece_passed(1_piece), false); + TEST_EQUAL(p->have_piece(0_piece), true); TEST_EQUAL(p->have_piece(1_piece), false); + TEST_EQUAL(p->is_piece_flushed(1_piece), false); // make sure write_failed() and lock_piece() actually // locks the piece, and that it won't be picked. @@ -2164,7 +2165,7 @@ TORRENT_TEST(num_pad_bytes_want_filter) TORRENT_TEST(num_pad_bytes_want_have) { auto p = setup_picker("111", " ", "444", ""); - p->we_have(1_piece); + p->piece_flushed(1_piece); TEST_CHECK((p->want() == piece_count{3, 0, true})); TEST_CHECK((p->have_want() == piece_count{1, 0, false})); TEST_CHECK((p->have() == piece_count{1, 0, false})); @@ -2193,7 +2194,7 @@ TORRENT_TEST(num_pad_bytes_we_have) p->set_pad_bytes(1_piece, 2); p->set_pad_bytes(0_piece, 0x4000); - p->we_have(1_piece); + p->piece_flushed(1_piece); TEST_CHECK((p->want() == piece_count{3, 0x4003, true})); TEST_CHECK((p->have_want() == piece_count{1, 2, false})); TEST_CHECK((p->have() == piece_count{1, 2, false})); @@ -2208,7 +2209,7 @@ TORRENT_TEST(num_pad_bytes_dont_want_have) p->set_pad_bytes(0_piece, 0x4000); p->set_piece_priority(1_piece, dont_download); - p->we_have(1_piece); + p->piece_flushed(1_piece); TEST_CHECK((p->want() == piece_count{2, 0x4001, true})); TEST_CHECK((p->have_want() == piece_count{0, 0, false})); TEST_CHECK((p->have() == piece_count{1, 2, false})); @@ -2222,7 +2223,7 @@ TORRENT_TEST(num_pad_bytes_have_dont_want) p->set_pad_bytes(1_piece, 2); p->set_pad_bytes(0_piece, 0x4000); - p->we_have(1_piece); + p->piece_flushed(1_piece); p->set_piece_priority(1_piece, dont_download); TEST_CHECK((p->want() == piece_count{2, 0x4001, true})); TEST_CHECK((p->have_want() == piece_count{0, 0, false})); @@ -2233,7 +2234,7 @@ TORRENT_TEST(num_pad_bytes_have_dont_want) TORRENT_TEST(have_dont_want_pad_bytes) { auto p = setup_picker("111", " ", "444", ""); - p->we_have(1_piece); + p->piece_flushed(1_piece); p->set_piece_priority(1_piece, dont_download); p->set_pad_bytes(2_piece, 1); p->set_pad_bytes(1_piece, 2); @@ -2250,38 +2251,38 @@ TORRENT_TEST(pad_bytes_have) { auto p = setup_picker("1111111", " ", "4444444", ""); p->set_pad_bytes(2_piece, 10); - TEST_CHECK(!p->have_piece(0_piece)); - TEST_CHECK(!p->have_piece(1_piece)); - TEST_CHECK(!p->have_piece(2_piece)); - TEST_CHECK(!p->have_piece(3_piece)); + TEST_CHECK(!p->is_piece_flushed(0_piece)); + TEST_CHECK(!p->is_piece_flushed(1_piece)); + TEST_CHECK(!p->is_piece_flushed(2_piece)); + TEST_CHECK(!p->is_piece_flushed(3_piece)); } { auto p = setup_picker("1111111", " ", "4444444", ""); p->set_pad_bytes(2_piece, default_block_size); - TEST_CHECK(!p->have_piece(0_piece)); - TEST_CHECK(!p->have_piece(1_piece)); - TEST_CHECK(!p->have_piece(2_piece)); - TEST_CHECK(!p->have_piece(3_piece)); + TEST_CHECK(!p->is_piece_flushed(0_piece)); + TEST_CHECK(!p->is_piece_flushed(1_piece)); + TEST_CHECK(!p->is_piece_flushed(2_piece)); + TEST_CHECK(!p->is_piece_flushed(3_piece)); } { auto p = setup_picker("1111111", " ", "4444444", ""); p->set_pad_bytes(2_piece, blocks_per_piece * default_block_size); - TEST_CHECK(!p->have_piece(0_piece)); - TEST_CHECK(!p->have_piece(1_piece)); - TEST_CHECK(p->have_piece(2_piece)); - TEST_CHECK(!p->have_piece(3_piece)); + TEST_CHECK(!p->is_piece_flushed(0_piece)); + TEST_CHECK(!p->is_piece_flushed(1_piece)); + TEST_CHECK(p->is_piece_flushed(2_piece)); + TEST_CHECK(!p->is_piece_flushed(3_piece)); } { auto p = setup_picker("1111111", " ", "4444444", ""); p->set_pad_bytes(2_piece, blocks_per_piece * default_block_size); p->set_pad_bytes(1_piece, default_block_size); - TEST_CHECK(!p->have_piece(0_piece)); - TEST_CHECK(!p->have_piece(1_piece)); - TEST_CHECK(p->have_piece(2_piece)); - TEST_CHECK(!p->have_piece(3_piece)); + TEST_CHECK(!p->is_piece_flushed(0_piece)); + TEST_CHECK(!p->is_piece_flushed(1_piece)); + TEST_CHECK(p->is_piece_flushed(2_piece)); + TEST_CHECK(!p->is_piece_flushed(3_piece)); } } @@ -2431,7 +2432,7 @@ TORRENT_TEST(pad_blocks_some_wanted_odd_blocks) auto p = std::make_shared( 3 * piece_size, piece_size); - p->we_have(1_piece); + p->piece_flushed(1_piece); p->set_piece_priority(1_piece, dont_download); p->set_pad_bytes(2_piece, 1); p->set_pad_bytes(1_piece, 2); @@ -2484,7 +2485,7 @@ TORRENT_TEST(mark_as_pad_whole_piece_seeding) { auto p = setup_picker("11", " ", "44", ""); p->set_pad_bytes(0_piece, 0x4000 * 4); - TEST_CHECK(p->have_piece(0_piece)); + TEST_CHECK(p->is_piece_flushed(0_piece)); TEST_CHECK(!p->is_seeding()); @@ -2772,8 +2773,8 @@ TORRENT_TEST(piece_extent_affinity_clear_done) // now all 5 extents are in use, if we finish a whole extent, it should be // removed from the list - p->we_have(0_piece); - p->we_have(1_piece); + p->piece_flushed(0_piece); + p->piece_flushed(1_piece); // we need to invoke the piece picker once to detect and reap this full // extent