diff --git a/ChangeLog b/ChangeLog index cce05906e..b158561cc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * introduced pop_alerts() which pops the entire alert queue in a single call * support saving metadata in resume file, enable it by default for magnet links * support for receiving multi announce messages for local peer discovery * added session::listen_no_system_port flag to prevent libtorrent from ever binding the listen socket to port 0 diff --git a/docs/manual.rst b/docs/manual.rst index ed108909b..32c54cc06 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1013,18 +1013,36 @@ Changes the mask of which alerts to receive. By default only errors are reported See alerts_ for mor information on the alert categories. -pop_alert() wait_for_alert() ----------------------------- +pop_alerts() pop_alert() wait_for_alert() +----------------------------------------- :: std::auto_ptr pop_alert(); + void pop_alerts(std::deque* alerts); alert const* wait_for_alert(time_duration max_wait); ``pop_alert()`` is used to ask the session if any errors or events has occurred. With `set_alert_mask()`_ you can filter which alerts to receive through ``pop_alert()``. For information about the alert categories, see alerts_. +``pop_alerts()`` pops all pending alerts in a single call. In high performance environments +with a very high alert churn rate, this can save significant amount of time compared to +popping alerts one at a time. Each call requires one round-trip to the network thread. If +alerts are produced in a higher rate than they can be popped (when popped one at a time) +it's easy to get stuck in an infinite loop, trying to drain the alert queue. Popping the entire +queue at once avoids this problem. + +However, the ``pop_alerts`` function comes with significantly more responsibility. You pass +in an *empty* ``std::dequeue`` to it. If it's not empty, all elements in it will +be deleted and then cleared. All currently pending alerts are returned by being swapped +into the passed in container. The responsibility of deleting the alerts is transferred +to the caller. This means you need to call delete for each item in the returned dequeue. +It's probably a good idea to delete the alerts as you handle them, to save one extra +pass over the dequeue. + +Alternatively, you can pass in the same container the next time you call ``pop_alerts``. + ``wait_for_alert`` blocks until an alert is available, or for no more than ``max_wait`` time. If ``wait_for_alert`` returns because of the time-out, and no alerts are available, it returns 0. If at least one alert was generated, a pointer to that alert is returned. diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 1921966de..7cc90d1ca 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -1468,21 +1468,22 @@ int main(int argc, char* argv[]) #endif // loop through the alert queue to see if anything has happened. - std::auto_ptr a; - a = ses.pop_alert(); + std::deque alerts; + ses.pop_alerts(&alerts); std::string now = time_now_string(); - while (a.get()) + for (std::deque::iterator i = alerts.begin() + , end(alerts.end()); i != end; ++i) { std::string event_string; - ::print_alert(a.get(), event_string); - ::handle_alert(ses, a.get(), files, non_files); + ::print_alert(*i, event_string); + ::handle_alert(ses, *i, files, non_files); events.push_back(event_string); if (events.size() >= 20) events.pop_front(); - - a = ses.pop_alert(); + delete *i; } + alerts.clear(); session_status sess_stat = ses.status(); @@ -1967,46 +1968,48 @@ int main(int argc, char* argv[]) alert const* a = ses.wait_for_alert(seconds(10)); if (a == 0) continue; - std::auto_ptr holder = ses.pop_alert(); + std::deque alerts; + ses.pop_alerts(&alerts); + std::string now = time_now_string(); + for (std::deque::iterator i = alerts.begin() + , end(alerts.end()); i != end; ++i) + { + // make sure to delete each alert + std::auto_ptr a(*i); - torrent_paused_alert const* tp = alert_cast(a); - if (tp) - { - ++num_paused; - printf("\rleft: %d failed: %d pause: %d " - , num_resume_data, num_failed, num_paused); - continue; - } + torrent_paused_alert const* tp = alert_cast(*i); + if (tp) + { + ++num_paused; + printf("\rleft: %d failed: %d pause: %d " + , num_resume_data, num_failed, num_paused); + continue; + } - save_resume_data_alert const* rd = alert_cast(a); -/* if (!rd) - { - std::string log; - ::print_alert(a, log); - printf("\n%s\n", log.c_str()); - } -*/ - if (alert_cast(a)) - { - ++num_failed; + save_resume_data_alert const* rd = alert_cast(*i); + if (alert_cast(*i)) + { + ++num_failed; + --num_resume_data; + printf("\rleft: %d failed: %d pause: %d " + , num_resume_data, num_failed, num_paused); + continue; + } + + if (!rd) continue; --num_resume_data; printf("\rleft: %d failed: %d pause: %d " , num_resume_data, num_failed, num_paused); - continue; + + if (!rd->resume_data) continue; + + torrent_handle h = rd->handle; + std::vector out; + bencode(std::back_inserter(out), *rd->resume_data); + save_file(combine_path(h.save_path(), ".resume/" + to_hex(h.info_hash().to_string()) + ".resume"), out); } - - if (!rd) continue; - --num_resume_data; - printf("\rleft: %d failed: %d pause: %d " - , num_resume_data, num_failed, num_paused); - - if (!rd->resume_data) continue; - - torrent_handle h = rd->handle; - std::vector out; - bencode(std::back_inserter(out), *rd->resume_data); - save_file(combine_path(h.save_path(), ".resume/" + to_hex(h.info_hash().to_string()) + ".resume"), out); } + if (g_log_file) fclose(g_log_file); printf("\nsaving session state\n"); { diff --git a/include/libtorrent/alert.hpp b/include/libtorrent/alert.hpp index 707ef9718..b77fa0da0 100644 --- a/include/libtorrent/alert.hpp +++ b/include/libtorrent/alert.hpp @@ -134,6 +134,7 @@ namespace libtorrent { void post_alert(const alert& alert_); bool pending() const; std::auto_ptr get(); + void get_all(std::deque* alerts); template bool should_post() const diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 850c6cb1d..a2ea8e065 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -285,6 +285,7 @@ namespace libtorrent void set_alert_mask(int m); size_t set_alert_queue_size_limit(size_t queue_size_limit_); std::auto_ptr pop_alert(); + void pop_alerts(std::deque* alerts); void set_alert_dispatch(boost::function)> const&); void post_alert(const alert& alert_); diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 1f87da233..d4da2e742 100644 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -430,7 +430,20 @@ namespace libtorrent int max_uploads() const TORRENT_DEPRECATED; #endif + // pop one alert from the alert queue, or do nothing + // and return a NULL pointer if there are no alerts + // in the queue std::auto_ptr pop_alert(); + + // pop all alerts in the alert queue and returns them + // in the supplied dequeue 'alerts'. The passed in + // queue must be empty when passed in. + // the responsibility of individual alerts returned + // in the dequeue is passed on to the caller of this function. + // when you're done with reacting to the alerts, you need to + // delete them all. + void pop_alerts(std::deque* alerts); + #ifndef TORRENT_NO_DEPRECATE TORRENT_DEPRECATED_PREFIX void set_severity_level(alert::severity_t s) TORRENT_DEPRECATED; diff --git a/src/alert.cpp b/src/alert.cpp index 38b752ac9..dbf30f76f 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -446,6 +446,13 @@ namespace libtorrent { return std::auto_ptr(result); } + void alert_manager::get_all(std::deque* alerts) + { + mutex::scoped_lock lock(m_mutex); + if (m_alerts.empty()) return; + m_alerts.swap(*alerts); + } + bool alert_manager::pending() const { mutex::scoped_lock lock(m_mutex); diff --git a/src/session.cpp b/src/session.cpp index 32aaf58c0..509cc96ce 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include @@ -978,6 +979,15 @@ namespace libtorrent return m_impl->pop_alert(); } + void session::pop_alerts(std::deque* alerts) + { + for (std::deque::iterator i = alerts->begin() + , end(alerts->end()); i != end; ++i) + delete *i; + alerts->clear(); + m_impl->pop_alerts(alerts); + } + alert const* session::wait_for_alert(time_duration max_wait) { return m_impl->wait_for_alert(max_wait); diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 27ba50a76..6a50c65bf 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -4766,12 +4766,14 @@ namespace aux { std::auto_ptr session_impl::pop_alert() { -// too expensive -// INVARIANT_CHECK; - return m_alerts.get(); } + void session_impl::pop_alerts(std::deque* alerts) + { + m_alerts.get_all(alerts); + } + alert const* session_impl::wait_for_alert(time_duration max_wait) { return m_alerts.wait_for_alert(max_wait); diff --git a/test/setup_transfer.cpp b/test/setup_transfer.cpp index c7065fd65..c5b864aaa 100644 --- a/test/setup_transfer.cpp +++ b/test/setup_transfer.cpp @@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include "libtorrent/session.hpp" #include "libtorrent/hasher.hpp" @@ -80,24 +81,24 @@ bool print_alerts(libtorrent::session& ses, char const* name TEST_CHECK(!handles.empty() || allow_no_torrents); torrent_handle h; if (!handles.empty()) h = handles[0]; - std::auto_ptr a; - a = ses.pop_alert(); - while (a.get()) + std::deque alerts; + ses.pop_alerts(&alerts); + for (std::deque::iterator i = alerts.begin(); i != alerts.end(); ++i) { - if (predicate && predicate(a.get())) ret = true; - if (peer_disconnected_alert* p = alert_cast(a.get())) + if (predicate && predicate(*i)) ret = true; + if (peer_disconnected_alert* p = alert_cast(*i)) { fprintf(stderr, "%s(%s): %s\n", name, print_endpoint(p->ip).c_str(), p->message().c_str()); } - else if (a->message() != "block downloading" - && a->message() != "block finished" - && a->message() != "piece finished") + else if ((*i)->message() != "block downloading" + && (*i)->message() != "block finished" + && (*i)->message() != "piece finished") { - fprintf(stderr, "%s: %s\n", name, a->message().c_str()); + fprintf(stderr, "%s: %s\n", name, (*i)->message().c_str()); } - TEST_CHECK(alert_cast(a.get()) == 0 || allow_failed_fastresume); + TEST_CHECK(alert_cast(*i) == 0 || allow_failed_fastresume); - peer_error_alert* pea = alert_cast(a.get()); + peer_error_alert* pea = alert_cast(*i); TEST_CHECK(pea == 0 || (!handles.empty() && h.status().is_seeding) || pea->error.message() == "connecting to peer" @@ -109,7 +110,7 @@ bool print_alerts(libtorrent::session& ses, char const* name || (allow_disconnects && pea->error.message() == "Broken pipe") || (allow_disconnects && pea->error.message() == "Connection reset by peer") || (allow_disconnects && pea->error.message() == "End of file.")); - a = ses.pop_alert(); + delete *i; } return ret; }