introduced pop_alerts() which pops the entire alert queue in a single call

This commit is contained in:
Arvid Norberg 2011-03-14 02:59:46 +00:00
parent a18f047311
commit 27c1bc632d
10 changed files with 114 additions and 57 deletions

View File

@ -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

View File

@ -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<alert> pop_alert();
void pop_alerts(std::deque<alert*>* 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<alert*>`` 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.

View File

@ -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<alert> a;
a = ses.pop_alert();
std::deque<alert*> alerts;
ses.pop_alerts(&alerts);
std::string now = time_now_string();
while (a.get())
for (std::deque<alert*>::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<alert> holder = ses.pop_alert();
std::deque<alert*> alerts;
ses.pop_alerts(&alerts);
std::string now = time_now_string();
for (std::deque<alert*>::iterator i = alerts.begin()
, end(alerts.end()); i != end; ++i)
{
// make sure to delete each alert
std::auto_ptr<alert> a(*i);
torrent_paused_alert const* tp = alert_cast<torrent_paused_alert>(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<torrent_paused_alert>(*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<save_resume_data_alert>(a);
/* if (!rd)
{
std::string log;
::print_alert(a, log);
printf("\n%s\n", log.c_str());
}
*/
if (alert_cast<save_resume_data_failed_alert>(a))
{
++num_failed;
save_resume_data_alert const* rd = alert_cast<save_resume_data_alert>(*i);
if (alert_cast<save_resume_data_failed_alert>(*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<char> 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<char> 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");
{

View File

@ -134,6 +134,7 @@ namespace libtorrent {
void post_alert(const alert& alert_);
bool pending() const;
std::auto_ptr<alert> get();
void get_all(std::deque<alert*>* alerts);
template <class T>
bool should_post() const

View File

@ -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<alert> pop_alert();
void pop_alerts(std::deque<alert*>* alerts);
void set_alert_dispatch(boost::function<void(std::auto_ptr<alert>)> const&);
void post_alert(const alert& alert_);

View File

@ -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<alert> 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<alert*>* alerts);
#ifndef TORRENT_NO_DEPRECATE
TORRENT_DEPRECATED_PREFIX
void set_severity_level(alert::severity_t s) TORRENT_DEPRECATED;

View File

@ -446,6 +446,13 @@ namespace libtorrent {
return std::auto_ptr<alert>(result);
}
void alert_manager::get_all(std::deque<alert*>* 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);

View File

@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <ctime>
#include <algorithm>
#include <set>
#include <deque>
#include <cctype>
#include <algorithm>
@ -978,6 +979,15 @@ namespace libtorrent
return m_impl->pop_alert();
}
void session::pop_alerts(std::deque<alert*>* alerts)
{
for (std::deque<alert*>::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);

View File

@ -4766,12 +4766,14 @@ namespace aux {
std::auto_ptr<alert> session_impl::pop_alert()
{
// too expensive
// INVARIANT_CHECK;
return m_alerts.get();
}
void session_impl::pop_alerts(std::deque<alert*>* 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);

View File

@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#include <fstream>
#include <deque>
#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<alert> a;
a = ses.pop_alert();
while (a.get())
std::deque<alert*> alerts;
ses.pop_alerts(&alerts);
for (std::deque<alert*>::iterator i = alerts.begin(); i != alerts.end(); ++i)
{
if (predicate && predicate(a.get())) ret = true;
if (peer_disconnected_alert* p = alert_cast<peer_disconnected_alert>(a.get()))
if (predicate && predicate(*i)) ret = true;
if (peer_disconnected_alert* p = alert_cast<peer_disconnected_alert>(*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<fastresume_rejected_alert>(a.get()) == 0 || allow_failed_fastresume);
TEST_CHECK(alert_cast<fastresume_rejected_alert>(*i) == 0 || allow_failed_fastresume);
peer_error_alert* pea = alert_cast<peer_error_alert>(a.get());
peer_error_alert* pea = alert_cast<peer_error_alert>(*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;
}