forked from premiere/premiere-libtorrent
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:
parent
eb39717b92
commit
7d3a8f5b19
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
329
src/torrent.cpp
329
src/torrent.cpp
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue