don't remove peers from the connection list immediately when disconnecting (#1782)

don't remove peers from the connection list immediately when disconnecting them, but defer it until later. This makes it less error prone to disconnect peers in loops over the connections
This commit is contained in:
Arvid Norberg 2017-03-07 13:50:03 -05:00 committed by GitHub
parent eb39717b92
commit 7d3a8f5b19
9 changed files with 252 additions and 243 deletions

View File

@ -167,6 +167,7 @@ nobase_include_HEADERS = \
aux_/disable_warnings_push.hpp \
aux_/disable_warnings_pop.hpp \
aux_/disk_job_fence.hpp \
aux_/deferred_handler.hpp \
aux_/dev_random.hpp \
aux_/deque.hpp \
aux_/escape_string.hpp \

View File

@ -0,0 +1,74 @@
/*
Copyright (c) 2017, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_DEFERRED_HANDLER_HPP
#define TORRENT_DEFERRED_HANDLER_HPP
#include "libtorrent/assert.hpp"
#include "libtorrent/io_service.hpp"
namespace libtorrent { namespace aux {
template <typename Handler>
struct handler_wrapper
{
handler_wrapper(bool& in_flight, Handler&& h)
: m_handler(std::move(h))
, m_in_flight(in_flight) {}
template <typename... Args>
void operator()(Args&&... a)
{
TORRENT_ASSERT(m_in_flight);
m_in_flight = false;
m_handler(std::forward<Args>(a)...);
}
private:
Handler m_handler;
bool& m_in_flight;
};
struct deferred_handler
{
template <typename Handler>
void post(io_service& ios, Handler&& h)
{
if (m_in_flight) return;
m_in_flight = true;
ios.post(handler_wrapper<Handler>(m_in_flight, std::forward<Handler>(h)));
}
private:
bool m_in_flight = false;
};
}}
#endif

View File

@ -43,6 +43,10 @@ namespace libtorrent { namespace aux {
Iter end() { return _end; }
};
template <typename Iter>
iterator_range<Iter> range(Iter begin, Iter end)
{ return { begin, end}; }
template <typename T, typename IndexType>
iterator_range<T*> range(vector<T, IndexType>& vec
, IndexType begin, IndexType end)

View File

@ -347,7 +347,7 @@ namespace libtorrent
peer_id const& get_peer_id() const override { return m_peer_id; }
void close_connection(peer_connection* p, error_code const& ec) override;
void close_connection(peer_connection* p) override;
void apply_settings_pack(std::shared_ptr<settings_pack> pack) override;
void apply_settings_pack_impl(settings_pack const& pack

View File

@ -189,7 +189,7 @@ namespace libtorrent { namespace aux
virtual peer_id const& get_peer_id() const = 0;
virtual void close_connection(peer_connection* p, error_code const& ec) = 0;
virtual void close_connection(peer_connection* p) = 0;
virtual int num_connections() const = 0;
virtual ses_buffer_holder allocate_buffer() = 0;

View File

@ -71,6 +71,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/suggest_piece.hpp"
#include "libtorrent/units.hpp"
#include "libtorrent/aux_/vector.hpp"
#include "libtorrent/aux_/deferred_handler.hpp"
#if TORRENT_COMPLETE_TYPES_REQUIRED
#include "libtorrent/peer_connection.hpp"
@ -640,7 +641,7 @@ namespace libtorrent
std::pair<peer_list::iterator, peer_list::iterator> find_peers(address const& a);
// the number of peers that belong to this torrent
int num_peers() const { return int(m_connections.size()); }
int num_peers() const { return int(m_connections.size() - m_peers_to_disconnect.size()); }
int num_seeds() const;
int num_downloaders() const;
@ -1105,6 +1106,9 @@ namespace libtorrent
private:
// trigger deferred disconnection of peers
void on_remove_peers();
void ip_filter_updated();
void inc_stats_counter(int c, int value = 1);
@ -1252,6 +1256,9 @@ namespace libtorrent
// verified its hash. If the hash fails, send reject to
// peers with outstanding requests, and dont_have to other
// peers. This vector is ordered, to make lookups fast.
// TODO: 3 factor out predictive pieces and all operations on it into a
// separate class (to use as memeber here instead)
std::vector<piece_index_t> m_predictive_pieces;
// the performance counters of this session
@ -1365,6 +1372,10 @@ namespace libtorrent
// monotonically increasing number for each added torrent
int m_sequence_number;
// used to post a message to defer disconnecting peers
std::vector<peer_connection*> m_peers_to_disconnect;
aux::deferred_handler m_deferred_disconnect;
// for torrents who have a bandwidth limit, this is != 0
// and refers to a peer_class in the session.
peer_class_t m_peer_class{0};

View File

@ -4238,6 +4238,8 @@ namespace libtorrent
handle, performance_alert::too_few_outgoing_ports);
}
m_disconnecting = true;
if (t)
{
if (ec)
@ -4293,24 +4295,26 @@ namespace libtorrent
check_invariant();
#endif
t->remove_peer(this);
// we need to do this here to maintain accurate accounting of number of
// unhoke slots. Ideally the updating of choked state and the
// accounting should be tighter
if (!m_choked)
{
m_choked = true;
m_counters.inc_stats_counter(counters::num_peers_up_unchoked_all, -1);
if (!ignore_unchoke_slots())
m_counters.inc_stats_counter(counters::num_peers_up_unchoked, -1);
}
}
else
{
TORRENT_ASSERT(m_download_queue.empty());
TORRENT_ASSERT(m_request_queue.empty());
m_ses.close_connection(this);
}
#if defined TORRENT_EXPENSIVE_INVARIANT_CHECKS
// since this connection doesn't have a torrent reference
// no torrent should have a reference to this connection either
TORRENT_ASSERT(!m_ses.any_torrent_has_peer(this));
#endif
m_disconnecting = true;
async_shutdown(*m_socket, m_socket);
m_ses.close_connection(this, ec);
}
bool peer_connection::ignore_unchoke_slots() const

View File

@ -977,14 +977,12 @@ namespace aux {
session_log(" aborting all connections (%d)", int(m_connections.size()));
#endif
// abort all connections
while (!m_connections.empty())
{
#if TORRENT_USE_ASSERTS
int conn = int(m_connections.size());
#endif
(*m_connections.begin())->disconnect(errors::stopping_torrent, op_bittorrent);
TORRENT_ASSERT_VAL(conn == int(m_connections.size()) + 1, conn);
}
// keep in mind that connections that are not associated with a torrent
// will remove its entry from m_connections immediately, which means we
// can't iterate over it here
auto conns = m_connections;
for (auto const& p : conns)
p->disconnect(errors::stopping_torrent, op_bittorrent);
// we need to give all the sockets an opportunity to actually have their handlers
// called and cancelled before we continue the shutdown. This is a bit
@ -2883,8 +2881,7 @@ namespace aux {
// currently expected to be scheduled for a connection
// with the connection queue, and should be cancelled
// TODO: should this function take a shared_ptr instead?
void session_impl::close_connection(peer_connection* p
, error_code const& ec)
void session_impl::close_connection(peer_connection* p)
{
TORRENT_ASSERT(is_single_thread());
std::shared_ptr<peer_connection> sp(p->self());
@ -2895,16 +2892,6 @@ namespace aux {
if (!sp.unique())
m_undead_peers.push_back(sp);
#ifndef TORRENT_DISABLE_LOGGING
if (should_log())
{
session_log(" CLOSING CONNECTION %s : %s"
, print_endpoint(p->remote()).c_str(), ec.message().c_str());
}
#else
TORRENT_UNUSED(ec);
#endif
TORRENT_ASSERT(p->is_disconnecting());
TORRENT_ASSERT(sp.use_count() > 0);
@ -6842,19 +6829,18 @@ namespace aux {
int unchokes_all = 0;
int num_optimistic = 0;
int disk_queue[2] = {0, 0};
for (connection_map::const_iterator i = m_connections.begin();
i != m_connections.end(); ++i)
for (auto const& p : m_connections)
{
TORRENT_ASSERT(*i);
std::shared_ptr<torrent> t = (*i)->associated_torrent().lock();
TORRENT_ASSERT(unique_peers.find(i->get()) == unique_peers.end());
unique_peers.insert(i->get());
TORRENT_ASSERT(p);
if (p->is_disconnecting()) continue;
if ((*i)->m_channel_state[0] & peer_info::bw_disk) ++disk_queue[0];
if ((*i)->m_channel_state[1] & peer_info::bw_disk) ++disk_queue[1];
std::shared_ptr<torrent> t = p->associated_torrent().lock();
TORRENT_ASSERT(unique_peers.find(p.get()) == unique_peers.end());
unique_peers.insert(p.get());
if (p->m_channel_state[0] & peer_info::bw_disk) ++disk_queue[0];
if (p->m_channel_state[1] & peer_info::bw_disk) ++disk_queue[1];
peer_connection* p = i->get();
TORRENT_ASSERT(!p->is_disconnecting());
if (p->ignore_unchoke_slots())
{
if (!p->is_choked()) ++unchokes_all;

View File

@ -95,6 +95,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/disk_interface.hpp"
#include "libtorrent/broadcast_socket.hpp" // for is_ip_address
#include "libtorrent/hex.hpp" // to_hex
#include "libtorrent/aux_/range.hpp"
// TODO: factor out cache_status to its own header
#include "libtorrent/disk_io_thread.hpp" // for cache_status
#include "libtorrent/aux_/numeric_cast.hpp"
@ -124,10 +125,6 @@ namespace libtorrent
return ret;
}
template <typename T, typename IndexType = int>
aux::vector<T, IndexType> clone(aux::vector<T, IndexType> const& v)
{ return v; }
} // anonymous namespace
web_seed_t::web_seed_t(web_seed_entry const& wse)
@ -814,8 +811,9 @@ namespace libtorrent
TORRENT_ASSERT(m_peer_class == peer_class_t{0});
TORRENT_ASSERT(m_connections.empty());
if (!m_connections.empty())
disconnect_all(errors::torrent_aborted, op_bittorrent);
// just in case, make sure the session accounting is kept right
for (auto p : m_connections)
m_ses.close_connection(p);
}
void torrent::read_piece(piece_index_t const piece)
@ -879,34 +877,19 @@ namespace libtorrent
if (share_mode()) return;
if (super_seeding()) return;
int idx = 0;
for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++idx)
for (auto p : m_connections)
{
// since the call to disconnect_if_redundant() may
// delete the entry from this container, make sure
// to increment the iterator early
peer_connection* p = *i;
if (p->type() == connection_type::bittorrent)
{
bt_peer_connection* btp = static_cast<bt_peer_connection*>(p);
std::shared_ptr<peer_connection> me(btp->self());
if (!btp->is_disconnecting())
{
btp->send_not_interested();
btp->write_upload_only();
}
}
if (p->type() != connection_type::bittorrent)
continue;
if (p->is_disconnecting())
// TODO: 3 the general peer interface should have a hook to be notified
// of upload only mode to avoid this downcast
bt_peer_connection* btp = static_cast<bt_peer_connection*>(p);
std::shared_ptr<peer_connection> me(btp->self());
if (!btp->is_disconnecting())
{
i = m_connections.begin() + idx;
--idx;
}
else
{
++i;
btp->send_not_interested();
btp->write_upload_only();
}
}
#endif
@ -949,10 +932,8 @@ namespace libtorrent
if (m_upload_mode)
{
// clear request queues of all peers
for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
for (auto p : m_connections)
{
peer_connection* p = (*i);
// we may want to disconnect other upload-only peers
if (p->upload_only())
p->update_interest();
@ -970,10 +951,8 @@ namespace libtorrent
}
// send_block_requests on all peers
for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
for (auto p : m_connections)
{
peer_connection* p = (*i);
// we may be interested now, or no longer interested
p->update_interest();
p->send_block_requests();
@ -1201,7 +1180,7 @@ namespace libtorrent
update_gauge();
for (auto const& p : m_connections)
for (auto p : m_connections)
{
peer_has(p->get_bitfield(), p);
}
@ -1719,7 +1698,7 @@ namespace libtorrent
// is available
// copy the peer list since peers may disconnect and invalidate
// m_connections as we initialize them
for (auto c : clone(m_connections))
for (auto c : m_connections)
{
auto pc = c->self();
if (pc->is_disconnecting()) continue;
@ -3177,7 +3156,7 @@ namespace libtorrent
return;
}
if (int(m_connections.size() - m_num_connecting) < 10)
if (num_peers() - m_num_connecting < 10)
{
// there are too few peers. Be conservative and don't assume it's
// well seeded until we can connect to more peers
@ -3770,7 +3749,7 @@ namespace libtorrent
// make a copy of the peer list since peers
// may disconnect while looping
for (auto c : clone(m_connections))
for (auto c : m_connections)
{
auto p = c->self();
@ -3801,7 +3780,7 @@ namespace libtorrent
// was the last piece we were interested in
// update_interest may disconnect the peer and
// invalidate the iterator
for (auto p : clone(m_connections))
for (auto p : m_connections)
{
// if we're not interested already, no need to check
if (!p->is_interesting()) continue;
@ -3930,22 +3909,22 @@ namespace libtorrent
// announce it to peers ahead of time to eliminate the
// round-trip times involved in announcing it, requesting it
// and sending it
void torrent::predicted_have_piece(piece_index_t const index, int milliseconds)
// TODO: 2 use chrono type for time duration
void torrent::predicted_have_piece(piece_index_t const index, int const milliseconds)
{
auto i = std::lower_bound(m_predictive_pieces.begin()
auto const i = std::lower_bound(m_predictive_pieces.begin()
, m_predictive_pieces.end(), index);
if (i != m_predictive_pieces.end() && *i == index) return;
for (peer_iterator p = m_connections.begin()
, end(m_connections.end()); p != end; ++p)
for (auto p : m_connections)
{
#ifndef TORRENT_DISABLE_LOGGING
(*p)->peer_log(peer_log_alert::outgoing, "PREDICTIVE_HAVE", "piece: %d expected in %d ms"
p->peer_log(peer_log_alert::outgoing, "PREDICTIVE_HAVE", "piece: %d expected in %d ms"
, static_cast<int>(index), milliseconds);
#else
TORRENT_UNUSED(milliseconds);
#endif
(*p)->announce_piece(index);
p->announce_piece(index);
}
m_predictive_pieces.insert(i, index);
@ -3974,15 +3953,14 @@ namespace libtorrent
, m_predictive_pieces.end(), index);
if (it != m_predictive_pieces.end() && *it == index)
{
for (peer_iterator p = m_connections.begin()
, end(m_connections.end()); p != end; ++p)
for (auto p : m_connections)
{
// send reject messages for
// potential outstanding requests to this piece
(*p)->reject_piece(index);
p->reject_piece(index);
// let peers that support the dont-have message
// know that we don't actually have this piece
(*p)->write_dont_have(index);
p->write_dont_have(index);
}
m_predictive_pieces.erase(it);
}
@ -4172,25 +4150,21 @@ namespace libtorrent
// loop over all peers and re-request potential duplicate
// blocks to this piece
for (std::vector<peer_connection*>::iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
for (auto p : m_connections)
{
peer_connection* p = *i;
std::vector<pending_block> const& dq = p->download_queue();
std::vector<pending_block> const& rq = p->request_queue();
for (std::vector<pending_block>::const_iterator k = dq.begin()
, end2(dq.end()); k != end2; ++k)
for (auto const& b : dq)
{
if (k->timed_out || k->not_wanted) continue;
if (k->block.piece_index != piece) continue;
m_picker->mark_as_downloading(k->block, p->peer_info_struct()
if (b.timed_out || b.not_wanted) continue;
if (b.block.piece_index != piece) continue;
m_picker->mark_as_downloading(b.block, p->peer_info_struct()
, p->picker_options());
}
for (std::vector<pending_block>::const_iterator k = rq.begin()
, end2(rq.end()); k != end2; ++k)
for (auto const& b : rq)
{
if (k->block.piece_index != piece) continue;
m_picker->mark_as_downloading(k->block, p->peer_info_struct()
if (b.block.piece_index != piece) continue;
m_picker->mark_as_downloading(b.block, p->peer_info_struct()
, p->picker_options());
}
}
@ -4297,6 +4271,10 @@ namespace libtorrent
// files belonging to the torrents
disconnect_all(errors::torrent_aborted, op_bittorrent);
// make sure to destruct the peers immediately
on_remove_peers();
TORRENT_ASSERT(m_connections.empty());
// post a message to the main thread to destruct
// the torrent object from there
if (m_storage)
@ -4306,18 +4284,11 @@ namespace libtorrent
}
else
{
TORRENT_ASSERT(m_abort);
if (alerts().should_post<cache_flushed_alert>())
alerts().emplace_alert<cache_flushed_alert>(get_handle());
}
// if this torrent still has connections, they may have buffers to return
// to the disk storage, in which case we cannot destroy the disk storage
// yet. wait until the last peer connection is removed
if (m_connections.empty())
{
m_storage.reset();
}
m_storage.reset();
// TODO: 2 abort lookups this torrent has made via the
// session host resolver interface
@ -4512,19 +4483,16 @@ namespace libtorrent
void torrent::cancel_non_critical()
{
std::set<piece_index_t> time_critical;
for (std::vector<time_critical_piece>::iterator i = m_time_critical_pieces.begin()
, end(m_time_critical_pieces.end()); i != end; ++i)
{
time_critical.insert(i->piece);
}
for (auto const& p : m_time_critical_pieces)
time_critical.insert(p.piece);
for (std::vector<peer_connection*>::iterator i
= m_connections.begin(), end(m_connections.end()); i != end; ++i)
for (auto p : m_connections)
{
// for each peer, go through its download and request queue
// and cancel everything, except pieces that are time critical
peer_connection* p = *i;
// make a copy of the download queue since we may be cancelling entries
// from it from within the loop
std::vector<pending_block> dq = p->download_queue();
for (std::vector<pending_block>::iterator k = dq.begin()
, end2(dq.end()); k != end2; ++k)
@ -4534,6 +4502,8 @@ namespace libtorrent
p->cancel_request(k->block, true);
}
// make a copy of the download queue since we may be cancelling entries
// from it from within the loop
std::vector<pending_block> rq = p->request_queue();
for (std::vector<pending_block>::const_iterator k = rq.begin()
, end2(rq.end()); k != end2; ++k)
@ -5327,13 +5297,15 @@ namespace libtorrent
{
TORRENT_ASSERT(p != nullptr);
TORRENT_ASSERT(is_single_thread());
TORRENT_ASSERT(std::count(m_peers_to_disconnect.begin()
, m_peers_to_disconnect.end(), p) == 0);
auto const i = sorted_find(m_connections, p);
if (i == m_connections.end())
{
TORRENT_ASSERT_FAIL();
return;
}
std::weak_ptr<torrent> weak_t = shared_from_this();
m_peers_to_disconnect.push_back(p);
m_deferred_disconnect.post(m_ses.get_io_service(), [=]() {
std::shared_ptr<torrent> t = weak_t.lock();
if (t) t->on_remove_peers();
});
torrent_peer* pp = p->peer_info_struct();
if (ready_for_connections())
@ -5341,16 +5313,13 @@ namespace libtorrent
TORRENT_ASSERT(p->associated_torrent().lock().get() == nullptr
|| p->associated_torrent().lock().get() == this);
if (p->is_seed())
if (has_picker())
{
if (has_picker())
if (p->is_seed())
{
m_picker->dec_refcount_all(pp);
}
}
else
{
if (has_picker())
else
{
auto const& pieces = p->get_bitfield();
TORRENT_ASSERT(pieces.count() <= int(pieces.size()));
@ -5392,8 +5361,27 @@ namespace libtorrent
peers_erased(st.erased);
p->set_peer_info(nullptr);
TORRENT_ASSERT(i != m_connections.end());
m_connections.erase(i);
update_want_peers();
update_want_tick();
}
void torrent::on_remove_peers()
{
TORRENT_ASSERT(is_single_thread());
INVARIANT_CHECK;
std::vector<peer_connection*> peers;
m_peers_to_disconnect.swap(peers);
for (peer_connection* p : peers)
{
TORRENT_ASSERT(p != nullptr);
auto const i = sorted_find(m_connections, p);
if (i != m_connections.end())
m_connections.erase(i);
m_ses.close_connection(p);
}
if (m_graceful_pause_mode && m_connections.empty())
{
@ -5448,7 +5436,7 @@ namespace libtorrent
TORRENT_ASSERT(!web->resolving);
if (web->resolving) return;
if (int(m_connections.size()) >= m_max_connections
if (num_peers() >= int(m_max_connections)
|| m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit))
return;
@ -5640,7 +5628,7 @@ namespace libtorrent
if (m_ses.is_aborted()) return;
if (int(m_connections.size()) >= m_max_connections
if (num_peers() >= int(m_max_connections)
|| m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit))
return;
@ -5736,7 +5724,7 @@ namespace libtorrent
#endif
}
if (int(m_connections.size()) >= m_max_connections
if (num_peers() >= int(m_max_connections)
|| m_ses.num_connections() >= settings().get_int(settings_pack::connections_limit))
return;
@ -6321,7 +6309,8 @@ namespace libtorrent
{
// this asserts that we don't have duplicates in the peer_list's peer list
peer_iterator i_ = std::find_if(m_connections.begin(), m_connections.end()
, [peerinfo] (peer_connection const* p) { return p->remote() == peerinfo->ip(); });
, [peerinfo] (peer_connection const* p)
{ return !p->is_disconnecting() && p->remote() == peerinfo->ip(); });
#if TORRENT_USE_I2P
TORRENT_ASSERT(i_ == m_connections.end()
|| (*i_)->type() != connection_type::bittorrent
@ -6486,13 +6475,6 @@ namespace libtorrent
}
TORRENT_CATCH (std::exception const&)
{
peer_iterator i = sorted_find(m_connections, c.get());
if (i != m_connections.end())
{
m_connections.erase(i);
update_want_peers();
update_want_tick();
}
c->disconnect(errors::no_error, op_bittorrent, 1);
return false;
}
@ -6556,20 +6538,8 @@ namespace libtorrent
, m_torrent_file->num_pieces());
// disconnect redundant peers
int idx = 0;
for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++idx)
{
if ((*i)->disconnect_if_redundant())
{
i = m_connections.begin() + idx;
--idx;
}
else
{
++i;
}
}
for (auto p : m_connections)
p->disconnect_if_redundant();
set_need_save_resume();
@ -6765,7 +6735,7 @@ namespace libtorrent
debug_log("CLOSING CONNECTION \"%s\" peer list full "
"connections: %d limit: %d"
, print_endpoint(p->remote()).c_str()
, int(m_connections.size())
, num_peers()
, m_max_connections);
}
#endif
@ -6786,7 +6756,7 @@ namespace libtorrent
}
#ifndef TORRENT_DISABLE_LOGGING
debug_log("incoming peer (%d)", int(m_connections.size()));
debug_log("incoming peer (%d)", num_peers());
#endif
#if TORRENT_USE_ASSERTS
@ -6816,7 +6786,7 @@ namespace libtorrent
debug_log("CLOSING CONNECTION \"%s\" peer list full (low peer rank) "
"connections: %d limit: %d"
, print_endpoint(peer->remote()).c_str()
, int(m_connections.size())
, num_peers()
, m_max_connections);
}
#endif
@ -6831,7 +6801,7 @@ namespace libtorrent
debug_log("CLOSING CONNECTION \"%s\" peer list full (low peer rank) "
"connections: %d limit: %d"
, print_endpoint(p->remote()).c_str()
, int(m_connections.size())
, num_peers()
, m_max_connections);
}
#endif
@ -6855,7 +6825,7 @@ namespace libtorrent
if (should_log())
{
debug_log("ATTACHED CONNECTION \"%s\" connections: %d limit: %d"
, print_endpoint(p->remote()).c_str(), int(m_connections.size())
, print_endpoint(p->remote()).c_str(), num_peers()
, m_max_connections);
}
#endif
@ -6867,7 +6837,7 @@ namespace libtorrent
{
if (m_abort) return false;
if (!m_connections.empty()) return true;
if (num_peers() > 0) return true;
// we might want to connect web seeds
if (!is_finished() && !m_web_seeds.empty() && m_files_checked)
@ -6929,7 +6899,7 @@ namespace libtorrent
bool torrent::want_peers() const
{
// if all our connection slots are taken, we can't connect to more
if (m_connections.size() >= m_max_connections) return false;
if (num_peers() >= int(m_max_connections)) return false;
// if we're paused, obviously we're not connecting to peers
if (is_paused() || m_abort || m_graceful_pause_mode) return false;
@ -7029,19 +6999,10 @@ namespace libtorrent
void torrent::disconnect_all(error_code const& ec, operation_t op)
{
while (!m_connections.empty())
for (auto const& p : m_connections)
{
peer_connection* p = *m_connections.begin();
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
#if TORRENT_USE_ASSERTS
std::size_t size = m_connections.size();
#endif
if (p->is_disconnecting())
m_connections.erase(m_connections.begin());
else
p->disconnect(ec, op);
TORRENT_ASSERT(m_connections.size() <= size);
p->disconnect(ec, op);
}
update_want_peers();
@ -7091,35 +7052,25 @@ namespace libtorrent
} // anonymous namespace
int torrent::disconnect_peers(int num, error_code const& ec)
int torrent::disconnect_peers(int const num, error_code const& ec)
{
INVARIANT_CHECK;
#if TORRENT_USE_ASSERTS
for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
{
// make sure this peer is not a dangling pointer
TORRENT_ASSERT(m_ses.has_peer(*i));
}
// make sure we don't have any dangling pointers
for (auto p : m_connections)
TORRENT_ASSERT(m_ses.has_peer(p));
#endif
int ret = 0;
while (ret < num && !m_connections.empty())
aux::vector<peer_connection*> to_disconnect;
to_disconnect.resize(num);
auto end = std::partial_sort_copy(m_connections.begin(), m_connections.end()
, to_disconnect.begin(), to_disconnect.end(), compare_disconnect_peer);
for (auto p : range(to_disconnect.begin(), end))
{
peer_iterator i = std::min_element(
m_connections.begin(), m_connections.end(), compare_disconnect_peer);
peer_connection* p = *i;
++ret;
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
#if TORRENT_USE_ASSERTS
int const num_conns = int(m_connections.size());
#endif
p->disconnect(ec, op_bittorrent);
TORRENT_ASSERT(int(m_connections.size()) == num_conns - 1);
}
return ret;
return static_cast<int>(end - to_disconnect.begin());
}
// called when torrent is finished (all interesting
@ -7381,20 +7332,17 @@ namespace libtorrent
}
#endif
bool const notify_initialized = !m_connections_initialized;
m_connections_initialized = true;
m_files_checked = true;
update_want_tick();
for (torrent::peer_iterator i = m_connections.begin();
i != m_connections.end();)
for (auto pc : m_connections)
{
peer_connection* pc = *i;
++i;
// all peer connections have to initialize themselves now that the metadata
// is available
if (!m_connections_initialized)
if (notify_initialized)
{
if (pc->is_disconnecting()) continue;
pc->on_metadata_impl();
@ -7674,13 +7622,13 @@ namespace libtorrent
int num_connecting = 0;
int num_connecting_seeds = 0;
std::map<piece_block, int> num_requests;
for (const_peer_iterator i = this->begin(); i != this->end(); ++i)
for (peer_connection const* peer : *this)
{
#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS
// make sure this peer is not a dangling pointer
TORRENT_ASSERT(m_ses.has_peer(*i));
TORRENT_ASSERT(m_ses.has_peer(peer));
#endif
peer_connection const& p = *(*i);
peer_connection const& p = *peer;
if (p.is_connecting()) ++num_connecting;
@ -7711,12 +7659,12 @@ namespace libtorrent
TORRENT_ASSERT(seeds == int(m_num_seeds));
TORRENT_ASSERT(num_connecting == int(m_num_connecting));
TORRENT_ASSERT(num_connecting_seeds == int(m_num_connecting_seeds));
TORRENT_ASSERT(int(m_num_uploads) <= int(m_connections.size()));
TORRENT_ASSERT(int(m_num_seeds) <= int(m_connections.size()));
TORRENT_ASSERT(int(m_num_connecting) <= int(m_connections.size()));
TORRENT_ASSERT(int(m_num_connecting_seeds) <= int(m_connections.size()));
TORRENT_ASSERT(int(m_num_uploads) <= num_peers());
TORRENT_ASSERT(int(m_num_seeds) <= num_peers());
TORRENT_ASSERT(int(m_num_connecting) <= num_peers());
TORRENT_ASSERT(int(m_num_connecting_seeds) <= num_peers());
TORRENT_ASSERT(int(m_num_connecting) + int(m_num_seeds) >= int(m_num_connecting_seeds));
TORRENT_ASSERT(int(m_num_connecting) + int(m_num_seeds) - int(m_num_connecting_seeds) <= int(m_connections.size()));
TORRENT_ASSERT(int(m_num_connecting) + int(m_num_seeds) - int(m_num_connecting_seeds) <= num_peers());
if (has_picker())
{
@ -8394,7 +8342,6 @@ namespace libtorrent
// disconnect all peers with no outstanding data to receive
// and choke all remaining peers to prevent responding to new
// requests
std::vector<peer_connection*> to_disconnect;
for (peer_connection* p : m_connections)
{
TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
@ -8413,11 +8360,6 @@ namespace libtorrent
continue;
}
to_disconnect.push_back(p);
}
for (peer_connection* p : to_disconnect)
{
// since we're currently in graceful pause mode, the last peer to
// disconnect (assuming all peers end up begin disconnected here)
// will post the torrent_paused_alert
@ -8436,13 +8378,12 @@ namespace libtorrent
{
TORRENT_ASSERT(is_single_thread());
auto it = m_connections.begin();
bool log_peers = it != m_connections.end()
&& (*it)->should_log(peer_log_alert::info);
bool const log_peers = !m_connections.empty()
&& m_connections.front()->should_log(peer_log_alert::info);
if (log_peers)
{
for (auto const& c : m_connections)
for (auto const c : m_connections)
{
c->peer_log(peer_log_alert::info, "TORRENT", "%s", message);
}
@ -8492,7 +8433,7 @@ namespace libtorrent
// maintained by the last peer to be disconnected in graceful pause mode,
// if there are no peers, we must not enter graceful pause mode, and post
// the torrent_paused_alert immediately instead.
if (m_connections.empty())
if (num_peers() == 0)
flags &= ~flag_graceful_pause;
if (m_paused == b)
@ -8894,26 +8835,14 @@ namespace libtorrent
maybe_connect_web_seeds();
m_swarm_last_seen_complete = m_last_seen_complete;
int idx = 0;
for (auto i = m_connections.begin(); i != m_connections.end(); ++idx)
for (auto p : m_connections)
{
// keep the peer object alive while we're
// inspecting it
std::shared_ptr<peer_connection> p = (*i)->self();
++i;
// look for the peer that saw a seed most recently
m_swarm_last_seen_complete = (std::max)(p->last_seen_complete(), m_swarm_last_seen_complete);
// updates the peer connection's ul/dl bandwidth
// resource requests
p->second_tick(tick_interval_ms);
if (p->is_disconnecting())
{
i = m_connections.begin() + idx;
--idx;
}
}
if (m_ses.alerts().should_post<stats_alert>())
m_ses.alerts().emplace_alert<stats_alert>(get_handle(), tick_interval_ms, m_stat);
@ -8991,7 +8920,7 @@ namespace libtorrent
// if we have everything we want we don't need to connect to any web-seed
if (!is_finished() && !m_web_seeds.empty() && m_files_checked
&& int(m_connections.size()) < m_max_connections
&& num_peers() < int(m_max_connections)
&& m_ses.num_connections() < settings().get_int(settings_pack::connections_limit))
{
// keep trying web-seeds if there are any
@ -9545,7 +9474,7 @@ namespace libtorrent
// request a block from. The earlier a peer is in the list,
// the sooner we will fully download the block we request.
aux::vector<peer_connection*> peers;
peers.reserve(m_connections.size());
peers.reserve(num_peers());
// some peers are marked as not being able to request time critical
// blocks from. For instance, peers that have choked us, peers that are
@ -10444,7 +10373,7 @@ namespace libtorrent
st->priority = priority;
#endif
st->num_peers = int(m_connections.size()) - m_num_connecting;
st->num_peers = num_peers() - m_num_connecting;
st->list_peers = m_peer_list ? m_peer_list->num_peers() : 0;
st->list_seeds = m_peer_list ? m_peer_list->num_seeds() : 0;
@ -10545,7 +10474,7 @@ namespace libtorrent
st->num_uploads = m_num_uploads;
st->uploads_limit = m_max_uploads == (1 << 24) - 1 ? -1 : m_max_uploads;
st->num_connections = int(m_connections.size());
st->num_connections = num_peers();
st->connections_limit = m_max_connections == (1 << 24) - 1 ? -1 : m_max_connections;
// if we don't have any metadata, stop here
@ -10672,7 +10601,7 @@ namespace libtorrent
TORRENT_ASSERT(is_single_thread());
INVARIANT_CHECK;
int const ret = int(m_connections.size())
int const ret = num_peers()
- m_num_seeds
- m_num_connecting
+ m_num_connecting_seeds;