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:
Arvid Norberg 2007-04-15 02:14:02 +00:00
parent c54f702e65
commit 40804a829b
8 changed files with 268 additions and 49 deletions

View File

@ -2127,6 +2127,8 @@ struct session_settings
int urlseed_pipeline_size;
int file_pool_size;
bool allow_multiple_connections_per_ip;
int max_failcount;
int min_reconnect_time;
bool use_dht_as_fallback;
};
</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
cases where simulations are run on the same machie, and all peers in a
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
(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

View File

@ -2112,6 +2112,8 @@ that will be sent to the tracker. The user-agent is a good way to identify your
int urlseed_pipeline_size;
int file_pool_size;
bool allow_multiple_connections_per_ip;
int max_failcount;
int min_reconnect_time;
bool use_dht_as_fallback;
};
@ -2205,6 +2207,14 @@ abusive behavior by peers. It may be useful to allow such connections in
cases where simulations are run on the same machie, and all peers in a
swarm has the same IP address.
``max_failcount`` is the maximum times we try to connect to a peer before
stop connecting again. If a peer succeeds, the failcounter is reset. If
a peer is retrieved from a peer source (other than DHT) the failcount is
decremented by one, allowing another try.
``min_reconnect_time`` is the time to wait between connection attempts. If
the peer fails, the time is multiplied by fail counter.
``use_dht_as_fallback`` determines how the DHT is used. If this is true
(which it is by default), the DHT will only be used for torrents where
all trackers in its tracker list has failed. Either by an explicit error

View File

@ -130,6 +130,12 @@ namespace libtorrent
// decreases the peer count for the given piece
// (used when a peer disconnects)
void dec_refcount(int index);
// these will increase and decrease the peer count
// of all pieces. They are used when seeds join
// or leave the swarm.
void inc_refcount_all();
void dec_refcount_all();
// This indicates that we just received this piece
// it means that the refcounter will indicate that

View File

@ -349,6 +349,21 @@ namespace libtorrent
}
#endif
}
void peer_has_all()
{
if (m_picker.get())
{
assert(!is_seed());
m_picker->inc_refcount_all();
}
#ifndef NDEBUG
else
{
assert(is_seed());
}
#endif
}
// when peer disconnects, this is called for every piece it had
void peer_lost(int index)

View File

@ -1259,6 +1259,8 @@ namespace libtorrent
if (m_supports_extensions) write_extensions();
#endif
// consider this a successful connection, reset the failcount
if (peer_info_struct()) peer_info_struct()->failcount = 0;
m_state = read_packet_size;
reset_recv_buffer(4);
}

View File

@ -260,41 +260,46 @@ namespace libtorrent
// now that we have a piece_picker,
// update it with this peers pieces
// build a vector of all pieces
m_num_pieces = 0;
bool interesting = false;
for (int i = 0; i < int(m_have_piece.size()); ++i)
{
if (m_have_piece[i])
{
++m_num_pieces;
t->peer_has(i);
// if the peer has a piece and we don't, the peer is interesting
if (!t->have_piece(i)
&& t->picker().piece_priority(i) != 0)
interesting = true;
}
}
if (m_num_pieces == int(m_have_piece.size()))
int num_pieces = std::count(m_have_piece.begin(), m_have_piece.end(), true);
if (num_pieces == int(m_have_piece.size()))
{
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << " *** THIS IS A SEED ***\n";
#endif
assert(m_peer_info);
m_peer_info->seed = true;
// if this is a web seed. we don't have a peer_info struct
if (m_peer_info) m_peer_info->seed = true;
// if we're a seed too, disconnect
if (t->is_seed())
{
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << " we're also a seed, disconnecting\n";
#endif
throw std::runtime_error("seed to seed connection redundant, disconnecting");
}
std::fill(m_have_piece.begin(), m_have_piece.end(), true);
m_num_pieces = num_pieces;
t->peer_has_all();
if (!t->is_finished())
t->get_policy().peer_is_interesting(*this);
return;
}
if (interesting)
t->get_policy().peer_is_interesting(*this);
m_num_pieces = num_pieces;
// if we're a seed, we don't keep track of piece availability
if (!t->is_seed())
{
bool interesting = false;
for (int i = 0; i < int(m_have_piece.size()); ++i)
{
if (m_have_piece[i])
{
t->peer_has(i);
// if the peer has a piece and we don't, the peer is interesting
if (!t->have_piece(i)
&& t->picker().piece_priority(i) != 0)
interesting = true;
}
}
if (interesting)
t->get_policy().peer_is_interesting(*this);
}
}
peer_connection::~peer_connection()
@ -795,32 +800,61 @@ namespace libtorrent
{
throw protocol_error("seed to seed connection redundant, disconnecting");
}
std::fill(m_have_piece.begin(), m_have_piece.end(), true);
m_num_pieces = num_pieces;
t->peer_has_all();
if (!t->is_finished())
t->get_policy().peer_is_interesting(*this);
return;
}
// let the torrent know which pieces the
// peer has
bool interesting = false;
for (int i = 0; i < (int)m_have_piece.size(); ++i)
// if we're a seed, we don't keep track of piece availability
if (!t->is_seed())
{
bool have = bitfield[i];
if (have && !m_have_piece[i])
bool interesting = false;
for (int i = 0; i < (int)m_have_piece.size(); ++i)
{
m_have_piece[i] = true;
++m_num_pieces;
t->peer_has(i);
if (!t->have_piece(i) && t->picker().piece_priority(i) != 0)
interesting = true;
bool have = bitfield[i];
if (have && !m_have_piece[i])
{
m_have_piece[i] = true;
++m_num_pieces;
t->peer_has(i);
if (!t->have_piece(i) && t->picker().piece_priority(i) != 0)
interesting = true;
}
else if (!have && m_have_piece[i])
{
// this should probably not be allowed
m_have_piece[i] = false;
--m_num_pieces;
t->peer_lost(i);
}
}
else if (!have && m_have_piece[i])
if (interesting) t->get_policy().peer_is_interesting(*this);
}
else
{
for (int i = 0; i < (int)m_have_piece.size(); ++i)
{
// this should probably not be allowed
m_have_piece[i] = false;
--m_num_pieces;
t->peer_lost(i);
bool have = bitfield[i];
if (have && !m_have_piece[i])
{
m_have_piece[i] = true;
++m_num_pieces;
}
else if (!have && m_have_piece[i])
{
// this should probably not be allowed
m_have_piece[i] = false;
--m_num_pieces;
}
}
}
if (interesting) t->get_policy().peer_is_interesting(*this);
}
// -----------------------------

View File

@ -249,12 +249,12 @@ namespace libtorrent
*/
}
#endif
/*
if (i->index == piece_pos::we_have_index)
{
assert(t == 0 || t->have_piece(index));
assert(i->downloading == 0);
/*
// make sure there's no entry
// with this index. (there shouldn't
// be since the piece_map is piece_pos::we_have_index)
@ -265,6 +265,7 @@ namespace libtorrent
assert(m_piece_info[i][j] != index);
}
}
*/
}
else
{
@ -278,7 +279,7 @@ namespace libtorrent
assert (i->index < vec.size());
assert(vec[i->index] == index);
}
/*
for (int k = 0; k < int(m_piece_info.size()); ++k)
{
for (int j = 0; j < int(m_piece_info[k].size()); ++j)
@ -287,8 +288,9 @@ namespace libtorrent
|| (prio > 0 && prio == k && int(i->index) == j));
}
}
}
*/
}
int count = std::count_if(m_downloads.begin(), m_downloads.end()
, has_index(index));
if (i->downloading == 1)
@ -589,6 +591,133 @@ namespace libtorrent
}
}
void piece_picker::inc_refcount_all()
{
TORRENT_PIECE_PICKER_INVARIANT_CHECK;
assert(m_files_checked_called);
// in general priority = availability * 2
// see piece_block::priority()
// this will insert two empty vectors at the start of the
// piece_info vector. It is done like this as an optimization,
// to swap vectors instead of copying them
while (m_piece_info.size() < 3
|| (!m_piece_info.rbegin()->empty())
|| (!(m_piece_info.rbegin()+1)->empty()))
{
m_piece_info.push_back(std::vector<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)
{
// TORRENT_PIECE_PICKER_INVARIANT_CHECK;

View File

@ -1291,12 +1291,27 @@ namespace libtorrent
{
assert(p->associated_torrent().lock().get() == this);
const std::vector<bool>& pieces = p->get_bitfield();
for (std::vector<bool>::const_iterator i = pieces.begin();
i != pieces.end(); ++i)
if (p->is_seed())
{
if (*i) peer_lost(static_cast<int>(i - pieces.begin()));
if (m_picker.get())
{
assert(!is_seed());
m_picker->dec_refcount_all();
}
}
else
{
// if we're a seed, we don't keep track of piece availability
if (!is_seed())
{
const std::vector<bool>& pieces = p->get_bitfield();
for (std::vector<bool>::const_iterator i = pieces.begin();
i != pieces.end(); ++i)
{
if (*i) peer_lost(static_cast<int>(i - pieces.begin()));
}
}
}
}