From 1c278cc697f590fefde44ea250c31e9036cdbaca Mon Sep 17 00:00:00 2001 From: arvidn Date: Fri, 10 Nov 2017 22:50:31 +0100 Subject: [PATCH] add API to query whether alerts have been dropped or not --- ChangeLog | 1 + include/libtorrent/alert.hpp | 6 ++--- include/libtorrent/alert_manager.hpp | 34 ++++++++++++++++++++------- include/libtorrent/alert_types.hpp | 6 ++--- include/libtorrent/session_handle.hpp | 14 +++++++++++ include/libtorrent/settings_pack.hpp | 6 ++++- src/alert_manager.cpp | 8 +++++++ src/session_handle.cpp | 7 ++++++ test/test_alert_manager.cpp | 20 ++++++++++++++++ 9 files changed, 86 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index be0458b4c..5933cd47a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * add API to query whether alerts have been dropped or not * add flags()/set_flags()/unset_flags() to torrent_handle, deprecate individual functions * added alert for block being sent to the send buffer * drop support for windows compilers without std::wstring diff --git a/include/libtorrent/alert.hpp b/include/libtorrent/alert.hpp index a918cb544..30509fd19 100644 --- a/include/libtorrent/alert.hpp +++ b/include/libtorrent/alert.hpp @@ -228,11 +228,11 @@ namespace libtorrent { // } // } // } - virtual int type() const = 0; + virtual int type() const noexcept = 0; // returns a string literal describing the type of the alert. It does // not include any information that might be bundled with the alert. - virtual char const* what() const = 0; + virtual char const* what() const noexcept = 0; // generate a string describing the alert and the information bundled // with it. This is mainly intended for debug and development use. It is not suitable @@ -242,7 +242,7 @@ namespace libtorrent { virtual std::string message() const = 0; // returns a bitmask specifying which categories this alert belong to. - virtual alert_category_t category() const = 0; + virtual alert_category_t category() const noexcept = 0; #ifndef TORRENT_NO_DEPRECATE diff --git a/include/libtorrent/alert_manager.hpp b/include/libtorrent/alert_manager.hpp index 8d6d52f04..187978d54 100644 --- a/include/libtorrent/alert_manager.hpp +++ b/include/libtorrent/alert_manager.hpp @@ -37,6 +37,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert.hpp" #include "libtorrent/heterogeneous_queue.hpp" #include "libtorrent/stack_allocator.hpp" +#include "libtorrent/alert_types.hpp" // for num_alert_types #include #include @@ -44,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace libtorrent { @@ -51,6 +53,10 @@ namespace libtorrent { struct plugin; #endif + // this bitset is used to indicate which alert types have been dropped since + // last queried. + using dropped_alerts_t = std::bitset; + class TORRENT_EXTRA_EXPORT alert_manager { public: @@ -58,8 +64,10 @@ namespace libtorrent { , alert_category_t alert_mask = alert::error_notification); ~alert_manager(); + dropped_alerts_t dropped_alerts(); + template - void emplace_alert(Args&&... args) + void emplace_alert(Args&&... args) try { std::unique_lock lock(m_mutex); @@ -69,12 +77,8 @@ namespace libtorrent { if (m_alerts[m_generation].size() >= m_queue_size_limit * (1 + T::priority)) { -// if (T::priority > 0) -// { - // TODO: there should be a way for the client to detect that an - // alert was dropped. Maybe add a flag to each m_alerts - // generation -// } + // record that we dropped an alert of this type + m_dropped.set(T::alert_type); return; } @@ -83,6 +87,12 @@ namespace libtorrent { maybe_notify(&alert, lock); } + catch (std::bad_alloc const&) + { + // record that we dropped an alert of this type + std::unique_lock lock(m_mutex); + m_dropped.set(T::alert_type); + } bool pending() const; void get_all(std::vector& alerts); @@ -105,12 +115,12 @@ namespace libtorrent { m_alert_mask = m; } - alert_category_t alert_mask() const + alert_category_t alert_mask() const noexcept { return m_alert_mask; } - int alert_queue_size_limit() const { return m_queue_size_limit; } + int alert_queue_size_limit() const noexcept { return m_queue_size_limit; } int set_alert_queue_size_limit(int queue_size_limit_); void set_notify_function(std::function const& fun); @@ -133,6 +143,12 @@ namespace libtorrent { std::atomic m_alert_mask; int m_queue_size_limit; + // a bitfield where each bit represents an alert type. Every time we drop + // an alert (because the queue is full or of some other error) we set the + // corresponding bit in this mask, to communicate to the client that it + // may have missed an update. + dropped_alerts_t m_dropped; + // this function (if set) is called whenever the number of alerts in // the alert queue goes from 0 to 1. The client is expected to wake up // its main message loop for it to poll for alerts (using get_alerts()). diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 3958da5cc..7e07ffa04 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -161,9 +161,9 @@ namespace libtorrent { name(name&&) noexcept = default; \ static const int priority = prio; \ static const int alert_type = seq; \ - virtual int type() const override { return alert_type; } \ - virtual alert_category_t category() const override { return static_category; } \ - virtual char const* what() const override { return #name; } + virtual int type() const noexcept override { return alert_type; } \ + virtual alert_category_t category() const noexcept override { return static_category; } \ + virtual char const* what() const noexcept override { return #name; } #define TORRENT_DEFINE_ALERT(name, seq) \ TORRENT_DEFINE_ALERT_IMPL(name, seq, 0) diff --git a/include/libtorrent/session_handle.hpp b/include/libtorrent/session_handle.hpp index 896205cfd..1438afbd1 100644 --- a/include/libtorrent/session_handle.hpp +++ b/include/libtorrent/session_handle.hpp @@ -45,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/io_service.hpp" #include "libtorrent/session_types.hpp" #include "libtorrent/portmap.hpp" // for portmap_protocol +#include "libtorrent/alert_manager.hpp" // for dropped_alerts_t #include "libtorrent/kademlia/dht_storage.hpp" #include "libtorrent/kademlia/dht_settings.hpp" @@ -934,9 +935,22 @@ namespace libtorrent { // retrieval of alerts should not be done in the callback. In fact, the // callback should not block. It should not perform any expensive work. // It really should just notify the main application thread. + // + // The ``dropped_alerts()`` function returns a ``std::bitfield`` + // representing which types of alerts have been dropped. Dropped meaning + // that the alert failed to be delivered to the client. The most common + // cause of such failure is that the internal alert queue grew too big + // (controlled by alert_queue_size). This call also clears the internal + // bitfield, so the bitfield starts recording dropped alerts from this + // point forward only. + // + // The type of an alert is returned by the polymorphic function + // ``alert::type()`` but can also be queries from a concrete type via + // ``T::alert_type``, as a static constant. void pop_alerts(std::vector* alerts); alert* wait_for_alert(time_duration max_wait); void set_alert_notify(std::function const& fun); + dropped_alerts_t dropped_alerts(); #ifndef TORRENT_NO_DEPRECATE #include "libtorrent/aux_/disable_warnings_push.hpp" diff --git a/include/libtorrent/settings_pack.hpp b/include/libtorrent/settings_pack.hpp index 8b816731f..6c3ebfdcf 100644 --- a/include/libtorrent/settings_pack.hpp +++ b/include/libtorrent/settings_pack.hpp @@ -1442,7 +1442,11 @@ namespace libtorrent { // ``alert_queue_size`` is the maximum number of alerts queued up // internally. If alerts are not popped, the queue will eventually - // fill up to this level. + // fill up to this level. Once the alert queue is full, additional + // alerts will be dropped, and not delievered to the client. Once the + // client drains the queue, new alerts may be delivered again. In order + // to know that alerts have been dropped, see + // session_handle::dropped_alerts(). alert_queue_size, // ``max_metadata_size`` is the maximum allowed size (in bytes) to be diff --git a/src/alert_manager.cpp b/src/alert_manager.cpp index 4cfe66028..26fc6730c 100644 --- a/src/alert_manager.cpp +++ b/src/alert_manager.cpp @@ -146,4 +146,12 @@ namespace libtorrent { std::swap(m_queue_size_limit, queue_size_limit_); return queue_size_limit_; } + + dropped_alerts_t alert_manager::dropped_alerts() + { + std::unique_lock lock(m_mutex); + dropped_alerts_t const ret = m_dropped; + m_dropped.reset(); + return ret; + } } diff --git a/src/session_handle.cpp b/src/session_handle.cpp index 4d6a3a2ea..c15ce4e4d 100644 --- a/src/session_handle.cpp +++ b/src/session_handle.cpp @@ -1130,6 +1130,13 @@ namespace { s->alerts().set_notify_function(fun); } + dropped_alerts_t session_handle::dropped_alerts() + { + std::shared_ptr s = m_impl.lock(); + if (!s) aux::throw_ex(errors::invalid_session_handle); + return s->alerts().dropped_alerts(); + } + #ifndef TORRENT_NO_DEPRECATE void session_handle::set_severity_level(alert::severity_t s) { diff --git a/test/test_alert_manager.cpp b/test/test_alert_manager.cpp index 6e966b12e..330f431a5 100644 --- a/test/test_alert_manager.cpp +++ b/test/test_alert_manager.cpp @@ -248,3 +248,23 @@ TORRENT_TEST(alert_mask) TEST_CHECK(!mgr.should_post()); } +TORRENT_TEST(dropped_alerts) +{ + alert_manager mgr(1, alert::all_categories); + + // nothing has dropped yet + TEST_CHECK(mgr.dropped_alerts().none()); + mgr.emplace_alert(torrent_handle()); + // still nothing, there's space for one alert + TEST_CHECK(mgr.dropped_alerts().none()); + mgr.emplace_alert(torrent_handle()); + // that last alert got dropped though, since it would have brought the queue + // size to 2 + auto const d = mgr.dropped_alerts(); + TEST_CHECK(d.count() == 1); + TEST_CHECK(d.test(torrent_finished_alert::alert_type)); + + // it should have been cleared now though + TEST_CHECK(mgr.dropped_alerts().none()); +} +