diff --git a/ChangeLog b/ChangeLog index cd976f1b4..4e82969d5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,10 @@ + * deliver notification of alerts being dropped via alerts_dropped_alert * deprecated alert::progress_notification alert category, split into finer grained categories * update plugin interface functions for improved type-safety * implemented support magnet URI extension, select specific file indices for download, BEP53 * make tracker keys multi-homed. remove set_key() function on session. - * 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_manager.hpp b/include/libtorrent/alert_manager.hpp index 7a9b9dd50..6aefdece5 100644 --- a/include/libtorrent/alert_manager.hpp +++ b/include/libtorrent/alert_manager.hpp @@ -54,10 +54,6 @@ 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: @@ -69,8 +65,6 @@ namespace libtorrent { ~alert_manager(); - dropped_alerts_t dropped_alerts(); - template void emplace_alert(Args&&... args) try { @@ -145,7 +139,7 @@ namespace libtorrent { // 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; + std::bitset 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 diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 278b44c7a..9efaaba6c 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -59,6 +59,8 @@ POSSIBILITY OF SUCH DAMAGE. #include #include "libtorrent/aux_/disable_warnings_pop.hpp" +#include + #if TORRENT_ABI_VERSION == 1 #define PROGRESS_NOTIFICATION | alert::progress_notification #else @@ -72,8 +74,14 @@ namespace libtorrent { TORRENT_DEPRECATED_EXPORT char const* operation_name(int op); #endif + // internal + TORRENT_EXTRA_EXPORT char const* alert_name(int alert_type); + // user defined alerts should use IDs greater than this - static const int user_alert_id = 10000; + constexpr int user_alert_id = 10000; + + // this constant represents "max_alert_index" + 1 + constexpr int num_alert_types = 96; enum alert_priority { @@ -190,7 +198,7 @@ TORRENT_VERSION_NAMESPACE_2 static const int alert_type = seq; \ 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; } + virtual char const* what() const noexcept override { return alert_name(alert_type); } #define TORRENT_DEFINE_ALERT(name, seq) \ TORRENT_DEFINE_ALERT_IMPL(name, seq, alert_priority_normal) @@ -1848,7 +1856,7 @@ TORRENT_VERSION_NAMESPACE_2 }; // This alert is posted whenever a tracker responds with a ``trackerid``. - // The tracker ID is like a cookie. The libtorrent will store the tracker ID + // The tracker ID is like a cookie. libtorrent will store the tracker ID // for this tracker and repeat it in subsequent announces. struct TORRENT_EXPORT trackerid_alert final : tracker_alert { @@ -2839,6 +2847,25 @@ TORRENT_VERSION_NAMESPACE_2 piece_index_t const piece_index; }; + // this alert is posted to indicate to the client that some alerts were + // 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). + struct TORRENT_EXPORT alerts_dropped_alert final : alert + { + explicit alerts_dropped_alert(aux::stack_allocator& alloc + , std::bitset const&); + TORRENT_DEFINE_ALERT_PRIO(alerts_dropped_alert, 95, alert_priority_critical + 1) + + static constexpr alert_category_t static_category = alert::error_notification; + std::string message() const override; + + // a bitmask indicating which alerts were dropped. Each bit represents the + // alert type ID, where bit 0 represents whether any alert of type 0 has + // been dropped, and so on. + std::bitset dropped_alerts; + }; + TORRENT_VERSION_NAMESPACE_2_END #undef TORRENT_DEFINE_ALERT_IMPL @@ -2846,7 +2873,6 @@ TORRENT_VERSION_NAMESPACE_2_END #undef TORRENT_DEFINE_ALERT_PRIO #undef PROGRESS_NOTIFICATION - constexpr int num_alert_types = 95; // this constant represents "max_alert_index" + 1 } #endif diff --git a/include/libtorrent/session_handle.hpp b/include/libtorrent/session_handle.hpp index e06e50abd..b06fe078f 100644 --- a/include/libtorrent/session_handle.hpp +++ b/include/libtorrent/session_handle.hpp @@ -46,7 +46,6 @@ 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" @@ -933,21 +932,12 @@ namespace libtorrent { // 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(); #if TORRENT_ABI_VERSION == 1 #include "libtorrent/aux_/disable_warnings_push.hpp" diff --git a/src/alert.cpp b/src/alert.cpp index 8b5854e3d..1dd9233ec 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -2540,6 +2540,83 @@ namespace { return ret; } + alerts_dropped_alert::alerts_dropped_alert(aux::stack_allocator& + , std::bitset const& dropped) + : dropped_alerts(dropped) + {} + + char const* alert_name(int const alert_type) + { + static std::array const names = {{ +#if TORRENT_ABI_VERSION == 1 + "torrent", "peer", "tracker", "torrent_added", +#else + "", "", "", "", +#endif + "torrent_removed", "read_piece", "file_completed", + "file_renamed", "file_rename_failed", "performance", + "state_changed", "tracker_error", "tracker_warning", + "scrape_reply", "scrape_failed", "tracker_reply", + "dht_reply", "tracker_announce", "hash_failed", + "peer_ban", "peer_unsnubbed", "peer_snubbed", + "peer_error", "peer_connect", "peer_disconnected", + "invalid_request", "torrent_finished", "piece_finished", + "request_dropped", "block_timeout", "block_finished", + "block_downloading", "unwanted_block", "storage_moved", + "storage_moved_failed", "torrent_deleted", + "torrent_delete_failed", "save_resume_data", + "save_resume_data_failed", "torrent_paused", + "torrent_resumed", "torrent_checked", "url_seed", + "file_error", "metadata_failed", "metadata_received", + "udp_error", "external_ip", "listen_failed", + "listen_succeeded", "portmap_error", "portmap", + "portmap_log", "fastresume_rejected", "peer_blocked", + "dht_announce", "dht_get_peers", "stats", + "cache_flushed", "anonymous_mode", "lsd_peer", + "trackerid", "dht_bootstrap", "", "torrent_error", + "torrent_need_cert", "incoming_connection", + "add_torrent", "state_update", +#if TORRENT_ABI_VERSION == 1 + "mmap_cache", +#else + "", +#endif + "session_stats", +#if TORRENT_ABI_VERSION == 1 + "torrent_update", +#else + "", +#endif + "", "dht_error", "dht_immutable_item", "dht_mutable_item", + "dht_put", "i2p", "dht_outgoing_get_peers", "log", + "torrent_log", "peer_log", "lsd_error", + "dht_stats", "incoming_request", "dht_log", + "dht_pkt", "dht_get_peers_reply", "dht_direct_response", + "picker_log", "session_error", "dht_live_nodes", + "session_stats_header", "dht_sample_infohashes", + "block_uploaded", "alerts_dropped" + }}; + + TORRENT_ASSERT(alert_type >= 0); + TORRENT_ASSERT(alert_type < num_alert_types); + return names[std::size_t(alert_type)]; + } + + std::string alerts_dropped_alert::message() const + { + std::string ret = "dropped alerts: "; + + TORRENT_ASSERT(int(dropped_alerts.size()) == num_alert_types); + for (int idx = 0; idx < num_alert_types; ++idx) + { + if (!dropped_alerts.test(std::size_t(idx))) continue; + ret += alert_name(idx); + ret += ' '; + } + + return ret; + } + // this will no longer be necessary in C++17 constexpr alert_category_t torrent_removed_alert::static_category; constexpr alert_category_t read_piece_alert::static_category; @@ -2628,6 +2705,7 @@ namespace { constexpr alert_category_t session_stats_header_alert::static_category; constexpr alert_category_t dht_sample_infohashes_alert::static_category; constexpr alert_category_t block_uploaded_alert::static_category; + constexpr alert_category_t alerts_dropped_alert::static_category; #if TORRENT_ABI_VERSION == 1 constexpr alert_category_t mmap_cache_alert::static_category; constexpr alert_category_t torrent_added_alert::static_category; diff --git a/src/alert_manager.cpp b/src/alert_manager.cpp index 01cff4f02..a86acfb3a 100644 --- a/src/alert_manager.cpp +++ b/src/alert_manager.cpp @@ -108,6 +108,11 @@ namespace libtorrent { if (m_alerts[m_generation].empty()) return; + if (m_dropped.any()) { + emplace_alert(m_dropped); + m_dropped.reset(); + } + m_alerts[m_generation].get_pointers(alerts); // swap buffers @@ -130,12 +135,4 @@ 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 692fbadab..bf93a5cd7 100644 --- a/src/session_handle.cpp +++ b/src/session_handle.cpp @@ -1144,13 +1144,6 @@ 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(); - } - #if TORRENT_ABI_VERSION == 1 void session_handle::set_severity_level(alert::severity_t s) { diff --git a/src/storage_utils.cpp b/src/storage_utils.cpp index 6cb4a5acb..47d1139d3 100644 --- a/src/storage_utils.cpp +++ b/src/storage_utils.cpp @@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/session.hpp" // for session::delete_files #include "libtorrent/stat_cache.hpp" #include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/torrent_status.hpp" #include diff --git a/test/test_alert_manager.cpp b/test/test_alert_manager.cpp index 94b967997..c9c4ccdd3 100644 --- a/test/test_alert_manager.cpp +++ b/test/test_alert_manager.cpp @@ -60,7 +60,8 @@ TORRENT_TEST(limit) mgr.get_all(alerts); // even though we posted 600, the limit was 500 - TEST_EQUAL(alerts.size(), 500); + // +1 for the alerts_dropped_alert + TEST_EQUAL(alerts.size(), 501); TEST_EQUAL(mgr.pending(), false); @@ -75,7 +76,8 @@ TORRENT_TEST(limit) mgr.get_all(alerts); // even though we posted 600, the limit was 200 - TEST_EQUAL(alerts.size(), 200); + // +1 for the alerts_dropped_alert + TEST_EQUAL(alerts.size(), 201); } TORRENT_TEST(priority_limit) @@ -97,7 +99,8 @@ TORRENT_TEST(priority_limit) // even though we posted 500, the limit was 100 for half of them and // 100 + 200 for the other half, meaning we should have 300 alerts now - TEST_EQUAL(alerts.size(), 300); + // +1 for the alerts_dropped_alert + TEST_EQUAL(alerts.size(), 301); } namespace { @@ -260,22 +263,33 @@ 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()); // 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 3 - auto const d = mgr.dropped_alerts(); + std::vector alerts; + mgr.get_all(alerts); + auto const d = alert_cast(alerts.back())->dropped_alerts; TEST_EQUAL(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()); +TORRENT_TEST(alerts_dropped_alert) +{ + alert_manager mgr(1, alert::all_categories); + + mgr.emplace_alert(torrent_handle()); + mgr.emplace_alert(torrent_handle()); + mgr.emplace_alert(torrent_handle()); + // that last alert got dropped though, since it would have brought the queue + // size to 3 + std::vector alerts; + mgr.get_all(alerts); + + TEST_EQUAL(alerts.back()->message(), "dropped alerts: torrent_finished "); } #ifndef TORRENT_DISABLE_EXTENSIONS diff --git a/test/test_alert_types.cpp b/test/test_alert_types.cpp index d97ed7e46..dfcc3c1c1 100644 --- a/test/test_alert_types.cpp +++ b/test/test_alert_types.cpp @@ -58,6 +58,7 @@ TORRENT_TEST(alerts_types) TEST_EQUAL(name::alert_type, seq); \ TEST_EQUAL(name::static_category, cat); \ TEST_EQUAL(count_alert_types, seq); \ + TEST_EQUAL(std::string(alert_name(name::alert_type)) + "_alert", #name); \ count_alert_types++; #if TORRENT_ABI_VERSION == 1 @@ -171,10 +172,11 @@ TORRENT_TEST(alerts_types) TEST_ALERT_TYPE(session_stats_header_alert, 92, 0, alert::stats_notification); TEST_ALERT_TYPE(dht_sample_infohashes_alert, 93, 0, alert::dht_operation_notification); TEST_ALERT_TYPE(block_uploaded_alert, 94, 0, PROGRESS_NOTIFICATION alert::upload_notification); + TEST_ALERT_TYPE(alerts_dropped_alert, 95, 3, alert::error_notification); #undef TEST_ALERT_TYPE - TEST_EQUAL(num_alert_types, 95); + TEST_EQUAL(num_alert_types, 96); TEST_EQUAL(num_alert_types, count_alert_types); }