add debug mechanism to assert the connection list is not mutated while iterating over it (#2016)

This commit is contained in:
Arvid Norberg 2017-05-21 11:36:35 -04:00 committed by GitHub
parent acd98d5c40
commit c7bb9f23ad
3 changed files with 55 additions and 0 deletions

View File

@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE.
#define TORRENT_DEBUG_HPP_INCLUDED #define TORRENT_DEBUG_HPP_INCLUDED
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"
#if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS #if TORRENT_USE_ASSERTS && defined BOOST_HAS_PTHREADS
#include <pthread.h> #include <pthread.h>
@ -204,6 +205,21 @@ namespace libtorrent
bool is_not_thread() const {return true; } bool is_not_thread() const {return true; }
}; };
#endif #endif
#if TORRENT_USE_ASSERTS
struct increment_guard
{
int& m_cnt;
increment_guard(int& c) : m_cnt(c) { TORRENT_ASSERT(m_cnt >= 0); ++m_cnt; }
~increment_guard() { --m_cnt; TORRENT_ASSERT(m_cnt >= 0); }
private:
increment_guard(increment_guard const&);
increment_guard operator=(increment_guard const&);
};
#define TORRENT_INCREMENT(x) increment_guard inc_(x)
#else
#define TORRENT_INCREMENT(x) do {} while (false)
#endif
} }
#endif // TORRENT_DEBUG_HPP_INCLUDED #endif // TORRENT_DEBUG_HPP_INCLUDED

View File

@ -1742,6 +1742,10 @@ namespace libtorrent
// set to true when torrent is start()ed. It may only be started once // set to true when torrent is start()ed. It may only be started once
bool m_was_started; bool m_was_started;
// this is set to true while we're looping over m_connections. We may not
// mutate the list while doing this
mutable int m_iterating_connections;
#endif #endif
}; };

View File

@ -286,6 +286,7 @@ namespace libtorrent
#if TORRENT_USE_ASSERTS #if TORRENT_USE_ASSERTS
, m_resume_data_loaded(false) , m_resume_data_loaded(false)
, m_was_started(false) , m_was_started(false)
, m_iterating_connections(0)
#endif #endif
{ {
// we cannot log in the constructor, because it relies on shared_from_this // we cannot log in the constructor, because it relies on shared_from_this
@ -1045,6 +1046,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin() for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i) , end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
if ((*i)->type() != peer_connection::bittorrent_connection) continue; if ((*i)->type() != peer_connection::bittorrent_connection) continue;
bt_peer_connection* p = static_cast<bt_peer_connection*>(*i); bt_peer_connection* p = static_cast<bt_peer_connection*>(*i);
p->write_share_mode(); p->write_share_mode();
@ -1062,6 +1064,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin(); for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++idx) i != m_connections.end(); ++idx)
{ {
TORRENT_INCREMENT(m_iterating_connections);
// since the call to disconnect_if_redundant() may // since the call to disconnect_if_redundant() may
// delete the entry from this container, make sure // delete the entry from this container, make sure
// to increment the iterator early // to increment the iterator early
@ -1131,6 +1134,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin() for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i) , end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = (*i); peer_connection* p = (*i);
// we may want to disconnect other upload-only peers // we may want to disconnect other upload-only peers
if (p->upload_only()) if (p->upload_only())
@ -1153,6 +1157,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin() for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i) , end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = (*i); peer_connection* p = (*i);
// we may be interested now, or no longer interested // we may be interested now, or no longer interested
p->update_interest(); p->update_interest();
@ -1373,6 +1378,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin() for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i) , end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_has((*i)->get_bitfield(), *i); peer_has((*i)->get_bitfield(), *i);
} }
} }
@ -1493,7 +1499,10 @@ namespace libtorrent
// suggest this piece to all peers // suggest this piece to all peers
for (peer_iterator i = m_connections.begin(); for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++i) i != m_connections.end(); ++i)
{
TORRENT_INCREMENT(m_iterating_connections);
(*i)->send_suggest(j->piece); (*i)->send_suggest(j->piece);
}
} }
void torrent::on_disk_tick_done(disk_io_job const* j) void torrent::on_disk_tick_done(disk_io_job const* j)
@ -1557,6 +1566,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin(); for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++i) i != m_connections.end(); ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = *i; peer_connection* p = *i;
boost::shared_ptr<peer_plugin> pp(tp->new_connection(peer_connection_handle(p->self()))); boost::shared_ptr<peer_plugin> pp(tp->new_connection(peer_connection_handle(p->self())));
if (pp) p->add_extension(pp); if (pp) p->add_extension(pp);
@ -2284,6 +2294,7 @@ namespace libtorrent
#ifndef TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_DISABLE_EXTENSIONS
for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
if ((*i)->type() != peer_connection::bittorrent_connection) continue; if ((*i)->type() != peer_connection::bittorrent_connection) continue;
bt_peer_connection* p = static_cast<bt_peer_connection*>(*i); bt_peer_connection* p = static_cast<bt_peer_connection*>(*i);
if (!p->supports_holepunch()) continue; if (!p->supports_holepunch()) continue;
@ -2301,6 +2312,7 @@ namespace libtorrent
{ {
for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) for (const_peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = *i; peer_connection* p = *i;
if (p->type() != peer_connection::bittorrent_connection) continue; if (p->type() != peer_connection::bittorrent_connection) continue;
if (p->remote() == ep) return static_cast<bt_peer_connection*>(p); if (p->remote() == ep) return static_cast<bt_peer_connection*>(p);
@ -2312,6 +2324,7 @@ namespace libtorrent
{ {
for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = *i; peer_connection* p = *i;
if (p->pid() == pid) return p; if (p->pid() == pid) return p;
} }
@ -4315,6 +4328,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin(); for (peer_iterator i = m_connections.begin();
i != m_connections.end();) i != m_connections.end();)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = *i; peer_connection* p = *i;
// update_interest may disconnect the peer and // update_interest may disconnect the peer and
// invalidate the iterator // invalidate the iterator
@ -4453,6 +4467,7 @@ namespace libtorrent
for (peer_iterator p = m_connections.begin() for (peer_iterator p = m_connections.begin()
, end(m_connections.end()); p != end; ++p) , end(m_connections.end()); p != end; ++p)
{ {
TORRENT_INCREMENT(m_iterating_connections);
#ifndef TORRENT_DISABLE_LOGGING #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"
, index, milliseconds); , index, milliseconds);
@ -4491,6 +4506,7 @@ namespace libtorrent
for (peer_iterator p = m_connections.begin() for (peer_iterator p = m_connections.begin()
, end(m_connections.end()); p != end; ++p) , end(m_connections.end()); p != end; ++p)
{ {
TORRENT_INCREMENT(m_iterating_connections);
// send reject messages for // send reject messages for
// potential outstanding requests to this piece // potential outstanding requests to this piece
(*p)->reject_piece(index); (*p)->reject_piece(index);
@ -4693,6 +4709,7 @@ namespace libtorrent
for (std::vector<peer_connection*>::iterator i = m_connections.begin() for (std::vector<peer_connection*>::iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i) , end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = *i; peer_connection* p = *i;
std::vector<pending_block> const& dq = p->download_queue(); std::vector<pending_block> const& dq = p->download_queue();
std::vector<pending_block> const& rq = p->request_queue(); std::vector<pending_block> const& rq = p->request_queue();
@ -4838,6 +4855,7 @@ namespace libtorrent
for (peer_iterator p = m_connections.begin() for (peer_iterator p = m_connections.begin()
, end(m_connections.end()); p != end; ++p) , end(m_connections.end()); p != end; ++p)
{ {
TORRENT_INCREMENT(m_iterating_connections);
(*p)->send_suggest(index); (*p)->send_suggest(index);
} }
@ -4920,6 +4938,7 @@ namespace libtorrent
for (const_peer_iterator j = m_connections.begin() for (const_peer_iterator j = m_connections.begin()
, end2(m_connections.end()); j != end2; ++j) , end2(m_connections.end()); j != end2; ++j)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* peer = *j; peer_connection* peer = *j;
if (peer->has_piece(p.piece_index)) ++p.num_peers; if (peer->has_piece(p.piece_index)) ++p.num_peers;
} }
@ -4942,7 +4961,10 @@ namespace libtorrent
{ {
for (peer_iterator p = m_connections.begin(); for (peer_iterator p = m_connections.begin();
p != m_connections.end(); ++p) p != m_connections.end(); ++p)
{
TORRENT_INCREMENT(m_iterating_connections);
(*p)->send_suggest(i->piece_index); (*p)->send_suggest(i->piece_index);
}
} }
} }
@ -5185,6 +5207,7 @@ namespace libtorrent
for (std::vector<peer_connection*>::iterator i for (std::vector<peer_connection*>::iterator i
= m_connections.begin(), end(m_connections.end()); i != end; ++i) = m_connections.begin(), end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
// for each peer, go through its download and request queue // for each peer, go through its download and request queue
// and cancel everything, except pieces that are time critical // and cancel everything, except pieces that are time critical
peer_connection* p = *i; peer_connection* p = *i;
@ -6025,6 +6048,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin() for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i) , end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
(*i)->cancel_request(block); (*i)->cancel_request(block);
} }
} }
@ -6207,6 +6231,7 @@ namespace libtorrent
peers_erased(st.erased); peers_erased(st.erased);
p->set_peer_info(0); p->set_peer_info(0);
TORRENT_ASSERT(m_iterating_connections == 0);
TORRENT_ASSERT(i != m_connections.end()); TORRENT_ASSERT(i != m_connections.end());
m_connections.erase(i); m_connections.erase(i);
@ -6679,6 +6704,7 @@ namespace libtorrent
{ {
TORRENT_ASSERT(!c->m_in_constructor); TORRENT_ASSERT(!c->m_in_constructor);
// add the newly connected peer to this torrent's peer list // add the newly connected peer to this torrent's peer list
TORRENT_ASSERT(m_iterating_connections == 0);
sorted_insert(m_connections, boost::get_pointer(c)); sorted_insert(m_connections, boost::get_pointer(c));
update_want_peers(); update_want_peers();
update_want_tick(); update_want_tick();
@ -7848,6 +7874,7 @@ namespace libtorrent
#endif #endif
// add the newly connected peer to this torrent's peer list // add the newly connected peer to this torrent's peer list
TORRENT_ASSERT(m_iterating_connections == 0);
sorted_insert(m_connections, boost::get_pointer(c)); sorted_insert(m_connections, boost::get_pointer(c));
m_ses.insert_peer(c); m_ses.insert_peer(c);
need_peer_list(); need_peer_list();
@ -7865,6 +7892,7 @@ namespace libtorrent
} }
TORRENT_CATCH (std::exception&) TORRENT_CATCH (std::exception&)
{ {
TORRENT_ASSERT(m_iterating_connections == 0);
peer_iterator i = sorted_find(m_connections, boost::get_pointer(c)); peer_iterator i = sorted_find(m_connections, boost::get_pointer(c));
if (i != m_connections.end()) if (i != m_connections.end())
{ {
@ -8171,6 +8199,7 @@ namespace libtorrent
return false; return false;
} }
TORRENT_ASSERT(sorted_find(m_connections, p) == m_connections.end()); TORRENT_ASSERT(sorted_find(m_connections, p) == m_connections.end());
TORRENT_ASSERT(m_iterating_connections == 0);
sorted_insert(m_connections, p); sorted_insert(m_connections, p);
update_want_peers(); update_want_peers();
update_want_tick(); update_want_tick();
@ -8424,6 +8453,7 @@ namespace libtorrent
// doesn't work with the !m_allow_peers -> m_num_peers == 0 condition // doesn't work with the !m_allow_peers -> m_num_peers == 0 condition
// INVARIANT_CHECK; // INVARIANT_CHECK;
TORRENT_ASSERT(m_iterating_connections == 0);
while (!m_connections.empty()) while (!m_connections.empty())
{ {
peer_connection* p = *m_connections.begin(); peer_connection* p = *m_connections.begin();
@ -8494,6 +8524,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin() for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i) , end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
// make sure this peer is not a dangling pointer // make sure this peer is not a dangling pointer
TORRENT_ASSERT(m_ses.has_peer(*i)); TORRENT_ASSERT(m_ses.has_peer(*i));
} }
@ -8553,6 +8584,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin(); for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++i) i != m_connections.end(); ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = *i; peer_connection* p = *i;
TORRENT_ASSERT(p->associated_torrent().lock().get() == this); TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
if (p->upload_only()) if (p->upload_only())
@ -8791,6 +8823,7 @@ namespace libtorrent
for (torrent::peer_iterator i = m_connections.begin(); for (torrent::peer_iterator i = m_connections.begin();
i != m_connections.end();) i != m_connections.end();)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* pc = *i; peer_connection* pc = *i;
++i; ++i;
@ -9882,6 +9915,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin(); for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++i) i != m_connections.end(); ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = *i; peer_connection* p = *i;
TORRENT_ASSERT(p->associated_torrent().lock().get() == this); TORRENT_ASSERT(p->associated_torrent().lock().get() == this);
@ -10535,6 +10569,7 @@ namespace libtorrent
for (peer_iterator i = m_connections.begin() for (peer_iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i) , end(m_connections.end()); i != end; ++i)
{ {
TORRENT_INCREMENT(m_iterating_connections);
peer_connection* p = *i; peer_connection* p = *i;
if (p->is_connecting()) continue; if (p->is_connecting()) continue;
if (p->is_disconnecting()) continue; if (p->is_disconnecting()) continue;