fix traversal_algorithm::done() being invoked more than once

A traversal can be done while there are still outstanding queries (i.e.
m_invoke_count is non-zero) if K good responses have already been received and
none of the outstanding queries are closer than the nodes which have responded.

When this happens and the outstanding queries eventually complete or timeout
they call traversal_algorithm::failed() or traversal_algorithm::finished() as
usual which will cause traversal_algorithm::done() to be called yet again.

The fix is to always set the done flag on all queried observers in
traversal_algorithm::done() so that the observers will refrain from calling
back into the traversal.

This also makes traversal_algorithm::abort() redundant since this was the only
thing it did before it called into done().
This commit is contained in:
Steven Siloti 2015-11-07 21:41:53 -08:00
parent 0c435b42b2
commit c2277b3ea5
4 changed files with 17 additions and 29 deletions

View File

@ -67,7 +67,6 @@ struct traversal_algorithm : boost::noncopyable
void failed(observer_ptr o, int flags = 0);
virtual ~traversal_algorithm();
void status(dht_lookup& l);
void abort();
void* allocate_observer();
void free_observer(void* ptr);

View File

@ -135,8 +135,6 @@ char const* find_data::name() const { return "find_data"; }
void find_data::done()
{
if (m_invoke_count != 0) return;
m_done = true;
#ifndef TORRENT_DISABLE_LOGGING

View File

@ -111,7 +111,7 @@ void get_item::got_data(bdecode_node const& v,
// There can only be one true immutable item with a given id
// Now that we've got it and the user doesn't want to do a put
// there's no point in continuing to query other nodes
abort();
done();
}
}
}

View File

@ -374,14 +374,22 @@ void traversal_algorithm::done()
#ifndef TORRENT_DISABLE_LOGGING
int results_target = m_node.m_table.bucket_size();
int closest_target = 160;
#endif
// TODO: 3 it would be nice to not have to perform this loop if
// logging is disabled
for (std::vector<observer_ptr>::iterator i = m_results.begin()
, end(m_results.end()); i != end && results_target > 0; ++i)
, end(m_results.end()); i != end; ++i)
{
boost::intrusive_ptr<observer> o = *i;
if ((o->flags & observer::flag_alive) && get_node().observer())
if (o->flags & observer::flag_queried)
{
// set the done flag on any outstanding queries to prevent them from
// calling finished() or failed() after we've already declared the traversal
// done
o->flags |= observer::flag_done;
}
#ifndef TORRENT_DISABLE_LOGGING
if (results_target > 0 && (o->flags & observer::flag_alive) && get_node().observer())
{
TORRENT_ASSERT(o->flags & observer::flag_queried);
char hex_id[41];
@ -395,8 +403,10 @@ void traversal_algorithm::done()
int dist = distance_exp(m_target, o->id());
if (dist < closest_target) closest_target = dist;
}
#endif
}
#ifndef TORRENT_DISABLE_LOGGING
if (get_node().observer())
{
get_node().observer()->log(dht_logger::traversal
@ -404,9 +414,11 @@ void traversal_algorithm::done()
, static_cast<void*>(this), closest_target, name());
}
#endif
// delete all our references to the observer objects so
// they will in turn release the traversal algorithm
m_results.clear();
m_invoke_count = 0;
}
bool traversal_algorithm::add_requests()
@ -610,26 +622,5 @@ void traversal_observer::reply(msg const& m)
set_id(node_id(id.string_ptr()));
}
void traversal_algorithm::abort()
{
for (std::vector<observer_ptr>::iterator i = m_results.begin()
, end(m_results.end()); i != end; ++i)
{
observer& o = **i;
if (o.flags & observer::flag_queried)
o.flags |= observer::flag_done;
}
#ifndef TORRENT_DISABLE_LOGGING
if (get_node().observer())
{
get_node().observer()->log(dht_logger::traversal, "[%p] ABORTED type: %s"
, static_cast<void*>(this), name());
}
#endif
done();
}
} } // namespace libtorrent::dht