forked from premiere/premiere-libtorrent
fix pad-file scalability issue
This commit is contained in:
parent
b729021625
commit
ca3ea591df
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
* fix pad-file scalability issue
|
||||||
* made coalesce_reads/coalesce_writes settings take effect on linux and windows
|
* made coalesce_reads/coalesce_writes settings take effect on linux and windows
|
||||||
* restore support for incoming connections over SOCKS5 (disabled by default)
|
* restore support for incoming connections over SOCKS5 (disabled by default)
|
||||||
* use unique peer_ids per connection
|
* use unique peer_ids per connection
|
||||||
|
|
|
@ -43,19 +43,12 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <boost/static_assert.hpp>
|
#include <boost/static_assert.hpp>
|
||||||
#include <boost/cstdint.hpp>
|
#include <boost/cstdint.hpp>
|
||||||
#include <boost/tuple/tuple.hpp>
|
#include <boost/tuple/tuple.hpp>
|
||||||
|
|
||||||
#ifdef TORRENT_DEBUG_REFCOUNTS
|
|
||||||
#include <set>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if TORRENT_USE_ASSERTS
|
|
||||||
#include <set>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
||||||
|
|
||||||
#include "libtorrent/peer_id.hpp"
|
#include "libtorrent/peer_id.hpp"
|
||||||
|
@ -353,6 +346,7 @@ namespace libtorrent
|
||||||
|
|
||||||
void mark_as_canceled(piece_block block, torrent_peer* peer);
|
void mark_as_canceled(piece_block block, torrent_peer* peer);
|
||||||
void mark_as_finished(piece_block block, torrent_peer* peer);
|
void mark_as_finished(piece_block block, torrent_peer* peer);
|
||||||
|
void mark_as_pad(piece_block block);
|
||||||
|
|
||||||
// prevent blocks from being picked from this piece.
|
// prevent blocks from being picked from this piece.
|
||||||
// to unlock the piece, call restore_piece() on it
|
// to unlock the piece, call restore_piece() on it
|
||||||
|
@ -756,6 +750,11 @@ namespace libtorrent
|
||||||
// TODO: should this be allocated lazily?
|
// TODO: should this be allocated lazily?
|
||||||
mutable std::vector<piece_pos> m_piece_map;
|
mutable std::vector<piece_pos> m_piece_map;
|
||||||
|
|
||||||
|
// this maps pieces to a range of blocks that are pad files and should not
|
||||||
|
// be picked
|
||||||
|
// TOOD: this could be a much more efficient data structure
|
||||||
|
std::set<piece_block> m_pad_blocks;
|
||||||
|
|
||||||
// the number of seeds. These are not added to
|
// the number of seeds. These are not added to
|
||||||
// the availability counters of the pieces
|
// the availability counters of the pieces
|
||||||
int m_seeds;
|
int m_seeds;
|
||||||
|
|
|
@ -238,7 +238,15 @@ namespace libtorrent
|
||||||
for (int i = 0; i < m_blocks_per_piece; ++i)
|
for (int i = 0; i < m_blocks_per_piece; ++i)
|
||||||
{
|
{
|
||||||
info[i].num_peers = 0;
|
info[i].num_peers = 0;
|
||||||
info[i].state = block_info::state_none;
|
if (m_pad_blocks.count(piece_block(piece, i)))
|
||||||
|
{
|
||||||
|
info[i].state = block_info::state_finished;
|
||||||
|
++ret.finished;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info[i].state = block_info::state_none;
|
||||||
|
}
|
||||||
info[i].peer = 0;
|
info[i].peer = 0;
|
||||||
#ifdef TORRENT_USE_VALGRIND
|
#ifdef TORRENT_USE_VALGRIND
|
||||||
VALGRIND_CHECK_VALUE_IS_DEFINED(info[i].peer);
|
VALGRIND_CHECK_VALUE_IS_DEFINED(info[i].peer);
|
||||||
|
@ -252,8 +260,13 @@ namespace libtorrent
|
||||||
VALGRIND_CHECK_VALUE_IS_DEFINED(ret.info_idx);
|
VALGRIND_CHECK_VALUE_IS_DEFINED(ret.info_idx);
|
||||||
VALGRIND_CHECK_VALUE_IS_DEFINED(ret.index);
|
VALGRIND_CHECK_VALUE_IS_DEFINED(ret.index);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
downloading_iter = m_downloads[download_state].insert(downloading_iter, ret);
|
downloading_iter = m_downloads[download_state].insert(downloading_iter, ret);
|
||||||
|
|
||||||
|
// in case every block was a pad block, we need to make sure the piece
|
||||||
|
// structure is correctly categorised
|
||||||
|
downloading_iter = update_piece_state(downloading_iter);
|
||||||
|
|
||||||
#if TORRENT_USE_INVARIANT_CHECKS
|
#if TORRENT_USE_INVARIANT_CHECKS
|
||||||
check_piece_state();
|
check_piece_state();
|
||||||
#endif
|
#endif
|
||||||
|
@ -2948,16 +2961,16 @@ get_out:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<piece_picker::downloading_piece>::iterator
|
std::vector<piece_picker::downloading_piece>::iterator
|
||||||
piece_picker::update_piece_state(
|
piece_picker::update_piece_state(
|
||||||
std::vector<piece_picker::downloading_piece>::iterator dp)
|
std::vector<piece_picker::downloading_piece>::iterator dp)
|
||||||
{
|
{
|
||||||
#ifdef TORRENT_PICKER_LOG
|
#ifdef TORRENT_PICKER_LOG
|
||||||
std::cerr << "[" << this << "] " << "update_piece_state(" << dp->index << ")" << std::endl;
|
std::cerr << "[" << this << "] " << "update_piece_state(" << dp->index << ")" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int num_blocks = blocks_in_piece(dp->index);
|
int const num_blocks = blocks_in_piece(dp->index);
|
||||||
piece_pos& p = m_piece_map[dp->index];
|
piece_pos& p = m_piece_map[dp->index];
|
||||||
int current_state = p.download_state;
|
int const current_state = p.download_state;
|
||||||
TORRENT_ASSERT(current_state != piece_pos::piece_open);
|
TORRENT_ASSERT(current_state != piece_pos::piece_open);
|
||||||
if (current_state == piece_pos::piece_open)
|
if (current_state == piece_pos::piece_open)
|
||||||
return dp;
|
return dp;
|
||||||
|
@ -3154,6 +3167,9 @@ get_out:
|
||||||
block_info* binfo = blocks_for_piece(*dp);
|
block_info* binfo = blocks_for_piece(*dp);
|
||||||
block_info& info = binfo[block.block_index];
|
block_info& info = binfo[block.block_index];
|
||||||
TORRENT_ASSERT(info.piece_index == block.piece_index);
|
TORRENT_ASSERT(info.piece_index == block.piece_index);
|
||||||
|
if (info.state == block_info::state_finished)
|
||||||
|
return false;
|
||||||
|
|
||||||
info.state = block_info::state_requested;
|
info.state = block_info::state_requested;
|
||||||
info.peer = peer;
|
info.peer = peer;
|
||||||
info.num_peers = 1;
|
info.num_peers = 1;
|
||||||
|
@ -3289,7 +3305,7 @@ get_out:
|
||||||
// if we already have this piece, just ignore this
|
// if we already have this piece, just ignore this
|
||||||
if (have_piece(block.piece_index)) return false;
|
if (have_piece(block.piece_index)) return false;
|
||||||
|
|
||||||
int prio = p.priority(this);
|
int const prio = p.priority(this);
|
||||||
TORRENT_ASSERT(prio < int(m_priority_boundries.size())
|
TORRENT_ASSERT(prio < int(m_priority_boundries.size())
|
||||||
|| m_dirty);
|
|| m_dirty);
|
||||||
p.download_state = piece_pos::piece_downloading;
|
p.download_state = piece_pos::piece_downloading;
|
||||||
|
@ -3303,6 +3319,11 @@ get_out:
|
||||||
TORRENT_ASSERT(&info >= &m_block_info[0]);
|
TORRENT_ASSERT(&info >= &m_block_info[0]);
|
||||||
TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size());
|
TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size());
|
||||||
TORRENT_ASSERT(info.piece_index == block.piece_index);
|
TORRENT_ASSERT(info.piece_index == block.piece_index);
|
||||||
|
|
||||||
|
TORRENT_ASSERT(info.state == block_info::state_none);
|
||||||
|
if (info.state == block_info::state_finished)
|
||||||
|
return false;
|
||||||
|
|
||||||
info.state = block_info::state_writing;
|
info.state = block_info::state_writing;
|
||||||
info.peer = peer;
|
info.peer = peer;
|
||||||
info.num_peers = 0;
|
info.num_peers = 0;
|
||||||
|
@ -3543,7 +3564,7 @@ get_out:
|
||||||
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int prio = p.priority(this);
|
int const prio = p.priority(this);
|
||||||
TORRENT_ASSERT(prio < int(m_priority_boundries.size())
|
TORRENT_ASSERT(prio < int(m_priority_boundries.size())
|
||||||
|| m_dirty);
|
|| m_dirty);
|
||||||
p.download_state = piece_pos::piece_downloading;
|
p.download_state = piece_pos::piece_downloading;
|
||||||
|
@ -3555,6 +3576,8 @@ get_out:
|
||||||
TORRENT_ASSERT(&info >= &m_block_info[0]);
|
TORRENT_ASSERT(&info >= &m_block_info[0]);
|
||||||
TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size());
|
TORRENT_ASSERT(&info < &m_block_info[0] + m_block_info.size());
|
||||||
TORRENT_ASSERT(info.piece_index == block.piece_index);
|
TORRENT_ASSERT(info.piece_index == block.piece_index);
|
||||||
|
if (info.state == block_info::state_finished)
|
||||||
|
return;
|
||||||
info.peer = peer;
|
info.peer = peer;
|
||||||
TORRENT_ASSERT(info.state == block_info::state_none);
|
TORRENT_ASSERT(info.state == block_info::state_none);
|
||||||
TORRENT_ASSERT(info.num_peers == 0);
|
TORRENT_ASSERT(info.num_peers == 0);
|
||||||
|
@ -3615,6 +3638,22 @@ get_out:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void piece_picker::mark_as_pad(piece_block block)
|
||||||
|
{
|
||||||
|
m_pad_blocks.insert(block);
|
||||||
|
// if we mark and entire piece as a pad file, we need to also
|
||||||
|
// consder that piece as "had" and increment some counters
|
||||||
|
typedef std::set<piece_block>::iterator iter;
|
||||||
|
iter begin = m_pad_blocks.lower_bound(piece_block(block.piece_index, 0));
|
||||||
|
int const blocks = blocks_in_piece(block.piece_index);
|
||||||
|
iter end = m_pad_blocks.upper_bound(piece_block(block.piece_index, blocks));
|
||||||
|
if (std::distance(begin, end) == blocks)
|
||||||
|
{
|
||||||
|
// the entire piece is a pad file
|
||||||
|
we_have(block.piece_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void piece_picker::mark_as_checking(int index)
|
void piece_picker::mark_as_checking(int index)
|
||||||
{
|
{
|
||||||
|
@ -3748,7 +3787,7 @@ get_out:
|
||||||
TORRENT_ASSERT(prev_prio < int(m_priority_boundries.size())
|
TORRENT_ASSERT(prev_prio < int(m_priority_boundries.size())
|
||||||
|| m_dirty);
|
|| m_dirty);
|
||||||
erase_download_piece(i);
|
erase_download_piece(i);
|
||||||
int prio = p.priority(this);
|
int const prio = p.priority(this);
|
||||||
if (!m_dirty)
|
if (!m_dirty)
|
||||||
{
|
{
|
||||||
if (prev_prio == -1 && prio >= 0) add(block.piece_index);
|
if (prev_prio == -1 && prio >= 0) add(block.piece_index);
|
||||||
|
|
|
@ -2044,7 +2044,7 @@ namespace libtorrent
|
||||||
for (; pr.length >= block; pr.length -= block, ++pb.block_index)
|
for (; pr.length >= block; pr.length -= block, ++pb.block_index)
|
||||||
{
|
{
|
||||||
if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
|
if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
|
||||||
m_picker->mark_as_finished(pb, 0);
|
m_picker->mark_as_pad(pb);
|
||||||
}
|
}
|
||||||
// ugly edge case where padfiles are not used they way they're
|
// ugly edge case where padfiles are not used they way they're
|
||||||
// supposed to be. i.e. added back-to back or at the end
|
// supposed to be. i.e. added back-to back or at the end
|
||||||
|
@ -3939,7 +3939,7 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fills in total_wanted, total_wanted_done and total_done
|
// fills in total_wanted, total_wanted_done and total_done
|
||||||
void torrent::bytes_done(torrent_status& st, bool accurate) const
|
void torrent::bytes_done(torrent_status& st, bool const accurate) const
|
||||||
{
|
{
|
||||||
INVARIANT_CHECK;
|
INVARIANT_CHECK;
|
||||||
|
|
||||||
|
@ -4004,7 +4004,7 @@ namespace {
|
||||||
if (m_picker->has_piece_passed(last_piece))
|
if (m_picker->has_piece_passed(last_piece))
|
||||||
{
|
{
|
||||||
TORRENT_ASSERT(st.total_done >= piece_size);
|
TORRENT_ASSERT(st.total_done >= piece_size);
|
||||||
int corr = m_torrent_file->piece_size(last_piece)
|
int const corr = m_torrent_file->piece_size(last_piece)
|
||||||
- piece_size;
|
- piece_size;
|
||||||
TORRENT_ASSERT(corr <= 0);
|
TORRENT_ASSERT(corr <= 0);
|
||||||
TORRENT_ASSERT(corr > -piece_size);
|
TORRENT_ASSERT(corr > -piece_size);
|
||||||
|
|
|
@ -1865,5 +1865,95 @@ TORRENT_TEST(piece_picker)
|
||||||
TEST_EQUAL(picked.size(), blocks_per_piece);
|
TEST_EQUAL(picked.size(), blocks_per_piece);
|
||||||
for (int i = 0; i < int(picked.size()); ++i)
|
for (int i = 0; i < int(picked.size()); ++i)
|
||||||
TEST_EQUAL(picked[0].piece_index, 4);
|
TEST_EQUAL(picked[0].piece_index, 4);
|
||||||
|
|
||||||
|
{
|
||||||
|
print_title("test mark_as_pad");
|
||||||
|
|
||||||
|
p = setup_picker("1111111", " ", "4444444", "");
|
||||||
|
piece_block const bl(2, 0);
|
||||||
|
p->mark_as_pad(bl);
|
||||||
|
|
||||||
|
bool ret = p->mark_as_downloading(piece_block(2, 1), NULL);
|
||||||
|
TEST_EQUAL(ret, true);
|
||||||
|
|
||||||
|
std::vector<piece_picker::downloading_piece> dl
|
||||||
|
= p->get_download_queue();
|
||||||
|
|
||||||
|
TEST_EQUAL(dl.size(), 1);
|
||||||
|
TEST_EQUAL(dl[0].finished, 1);
|
||||||
|
TEST_EQUAL(dl[0].writing, 0);
|
||||||
|
TEST_EQUAL(dl[0].requested, 1);
|
||||||
|
TEST_EQUAL(dl[0].index, 2);
|
||||||
|
|
||||||
|
piece_picker::block_info* blocks = p->blocks_for_piece(dl[0]);
|
||||||
|
TEST_EQUAL(blocks[0].state, piece_picker::block_info::state_finished);
|
||||||
|
TEST_EQUAL(blocks[1].state, piece_picker::block_info::state_requested);
|
||||||
|
TEST_EQUAL(blocks[2].state, piece_picker::block_info::state_none);
|
||||||
|
TEST_EQUAL(blocks[3].state, piece_picker::block_info::state_none);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
print_title("test mark_as_pad downloading");
|
||||||
|
|
||||||
|
p = setup_picker("1111111", " ", "4444444", "");
|
||||||
|
piece_block const bl(2, 0);
|
||||||
|
p->mark_as_pad(bl);
|
||||||
|
|
||||||
|
bool ret = p->mark_as_downloading(piece_block(2, 0), NULL);
|
||||||
|
TEST_EQUAL(ret, false);
|
||||||
|
|
||||||
|
std::vector<piece_picker::downloading_piece> dl
|
||||||
|
= p->get_download_queue();
|
||||||
|
|
||||||
|
TEST_EQUAL(dl.size(), 1);
|
||||||
|
TEST_EQUAL(dl[0].finished, 1);
|
||||||
|
TEST_EQUAL(dl[0].writing, 0);
|
||||||
|
TEST_EQUAL(dl[0].requested, 0);
|
||||||
|
TEST_EQUAL(dl[0].index, 2);
|
||||||
|
|
||||||
|
piece_picker::block_info* blocks = p->blocks_for_piece(dl[0]);
|
||||||
|
TEST_EQUAL(blocks[0].state, piece_picker::block_info::state_finished);
|
||||||
|
TEST_EQUAL(blocks[1].state, piece_picker::block_info::state_none);
|
||||||
|
TEST_EQUAL(blocks[2].state, piece_picker::block_info::state_none);
|
||||||
|
TEST_EQUAL(blocks[3].state, piece_picker::block_info::state_none);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
print_title("test mark_as_pad seeding");
|
||||||
|
|
||||||
|
p = setup_picker("1", " ", "4", "");
|
||||||
|
p->mark_as_pad(piece_block(0, 0));
|
||||||
|
p->mark_as_pad(piece_block(0, 1));
|
||||||
|
p->mark_as_pad(piece_block(0, 2));
|
||||||
|
|
||||||
|
TEST_CHECK(!p->is_seeding());
|
||||||
|
|
||||||
|
p->mark_as_finished(piece_block(0, 3), NULL);
|
||||||
|
|
||||||
|
TEST_CHECK(!p->is_seeding());
|
||||||
|
p->piece_passed(0);
|
||||||
|
TEST_CHECK(p->is_seeding());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
print_title("test mark_as_pad whole pad piece, seeding");
|
||||||
|
|
||||||
|
p = setup_picker("11", " ", "44", "");
|
||||||
|
p->mark_as_pad(piece_block(0, 0));
|
||||||
|
p->mark_as_pad(piece_block(0, 1));
|
||||||
|
p->mark_as_pad(piece_block(0, 2));
|
||||||
|
p->mark_as_pad(piece_block(0, 3));
|
||||||
|
|
||||||
|
TEST_CHECK(!p->is_seeding());
|
||||||
|
|
||||||
|
p->mark_as_finished(piece_block(1, 0), NULL);
|
||||||
|
p->mark_as_finished(piece_block(1, 1), NULL);
|
||||||
|
p->mark_as_finished(piece_block(1, 2), NULL);
|
||||||
|
p->mark_as_finished(piece_block(1, 3), NULL);
|
||||||
|
|
||||||
|
TEST_CHECK(!p->is_seeding());
|
||||||
|
p->piece_passed(1);
|
||||||
|
TEST_CHECK(p->is_seeding());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue