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