diff --git a/ChangeLog b/ChangeLog index 52da5ec38..e75eba572 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * introduced a more scalable API for torrent status updates (post_torrent_updates()) * updated the API to add_torrent_params turning all bools into flags of a flags field * added async_add_torrent() function to significantly improve performance when adding many torrents diff --git a/docs/manual.rst b/docs/manual.rst index 140d2afad..ba92334de 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -394,7 +394,8 @@ async_add_torrent() add_torrent() flag_paused = 0x020, flag_auto_managed = 0x040. flag_duplicate_is_error = 0x080, - flag_merge_resume_trackers = 0x100 + flag_merge_resume_trackers = 0x100, + flag_update_subscribe = 0x200 }; int version; @@ -524,7 +525,8 @@ and how it's added. These are the flags:: flag_paused = 0x020, flag_auto_managed = 0x040. flag_duplicate_is_error = 0x080, - flag_merge_resume_trackers = 0x100 + flag_merge_resume_trackers = 0x100, + flag_update_subscribe = 0x200 } ``flag_apply_ip_filter`` determines if the IP filter should apply to this torrent or not. By @@ -535,6 +537,9 @@ an auto-update torrent for instance. ``flag_merge_resume_trackers`` defaults to off and specifies whether tracker URLs loaded from resume data should be added to the trackers in the torrent or replace the trackers. +``flag_update_subscribe`` is on by default and means that this torrent will be part of state +updates when calling `post_torrent_updates()`_. + ``flag_paused`` specifies whether or not the torrent is to be started in a paused state. I.e. it won't connect to the tracker or any of the peers until it's resumed. This is typically a good way of avoiding race conditions when setting @@ -637,6 +642,11 @@ get_torrent_status() refresh_torrent_status() void refresh_torrent_status(std::vector* ret , boost::uint32_t flags = 0) const; +.. note:: + these calls are potentially expensive and won't scale well + with lots of torrents. If you're concerned about performance, consider + using ``post_torrent_updates()`` instead. + ``get_torrent_status`` returns a vector of the ``torrent_status`` for every torrent which satisfies ``pred``, which is a predicate function which determines if a torrent should be included in the returned set or not. Returning true means @@ -656,6 +666,21 @@ if you have a lot of torrents. Any ``torrent_status`` object whose ``handle`` member is not referring to a valid torrent are ignored. +post_torrent_updates() +---------------------- + + :: + + void post_torrent_updates(); + +This functions instructs the session to post the state_update_alert_, containing +the status of all torrents whose state changed since the last time this function +was called. + +Only torrents who has the state subscription flag set will be included. This flag +is on by default. See ``add_torrent_params`` under `async_add_torrent() add_torrent()`_. + + load_asnum_db() load_country_db() as_for_ip() --------------------------------------------- @@ -7430,6 +7455,28 @@ as: ``ip`` is the IP address and port the connection came from. +state_update_alert +------------------ + +This alert is only posted when requested by the user, by calling `post_torrent_updates()`_ +on the session. It contains the torrent status of all torrents that changed +since last time this message was posted. Its category is ``status_notification``, but +it's not subject to filtering, since it's only manually posted anyway. + +:: + + + struct state_update_alert: alert + { + // ... + std::vector status; + }; + +``status`` contains the torrent status of all torrents that changed since last time +this message was posted. Note that you can map a torrent status to a specific torrent +via its ``handle`` member. The receiving end is suggested to have all torrents sorted +by the ``torrent_handle`` or hashed by it, for efficient updates. + alert dispatcher ================ diff --git a/include/libtorrent/add_torrent_params.hpp b/include/libtorrent/add_torrent_params.hpp index b7076b0b8..0b8395419 100644 --- a/include/libtorrent/add_torrent_params.hpp +++ b/include/libtorrent/add_torrent_params.hpp @@ -57,7 +57,7 @@ namespace libtorrent , userdata(0) , file_priorities(0) #ifndef TORRENT_NO_DEPRECATE - , flags(flag_ignore_flags) + , flags(flag_ignore_flags | default_flags) , seed_mode(false) , override_resume_data(false) , upload_mode(false) @@ -68,26 +68,30 @@ namespace libtorrent , duplicate_is_error(false) , merge_resume_trackers(false) #else - , flags(flag_apply_ip_filter | flag_paused | flag_auto_managed) + , flags(default_flags) #endif { -#ifndef TORRENT_NO_DEPRECATE - if (flags == flag_ignore_flags) - { - flags = 0; - if (seed_mode) flags |= flag_seed_mode; - if (override_resume_data) flags |= flag_override_resume_data; - if (upload_mode) flags |= flag_upload_mode; - if (share_mode) flags |= flag_share_mode; - if (apply_ip_filter) flags |= flag_apply_ip_filter; - if (paused) flags |= flag_paused; - if (auto_managed) flags |= flag_auto_managed; - if (duplicate_is_error) flags |= flag_duplicate_is_error; - if (merge_resume_trackers) flags |= flag_merge_resume_trackers; - } -#endif } +#ifndef TORRENT_NO_DEPRECATE + void update_flags() const + { + if (flags != (flag_ignore_flags | default_flags)) return; + + boost::uint64_t& f = const_cast(flags); + f = flag_update_subscribe; + if (seed_mode) f |= flag_seed_mode; + if (override_resume_data) f |= flag_override_resume_data; + if (upload_mode) f |= flag_upload_mode; + if (share_mode) f |= flag_share_mode; + if (apply_ip_filter) f |= flag_apply_ip_filter; + if (paused) f |= flag_paused; + if (auto_managed) f |= flag_auto_managed; + if (duplicate_is_error) f |= flag_duplicate_is_error; + if (merge_resume_trackers) f |= flag_merge_resume_trackers; + } +#endif + enum flags_t { flag_seed_mode = 0x001, @@ -98,8 +102,10 @@ namespace libtorrent flag_paused = 0x020, flag_auto_managed = 0x040, flag_duplicate_is_error = 0x080, - flag_merge_resume_trackers = 0x100 + flag_merge_resume_trackers = 0x100, + flag_update_subscribe = 0x200, + default_flags = flag_update_subscribe | flag_auto_managed | flag_paused | flag_apply_ip_filter #ifndef TORRENT_NO_DEPRECATE , flag_ignore_flags = 0x80000000 #endif diff --git a/include/libtorrent/alert.hpp b/include/libtorrent/alert.hpp index e8b4e2730..3a2989f19 100644 --- a/include/libtorrent/alert.hpp +++ b/include/libtorrent/alert.hpp @@ -133,6 +133,7 @@ namespace libtorrent { ~alert_manager(); void post_alert(const alert& alert_); + void post_alert_ptr(alert* alert_); bool pending() const; std::auto_ptr get(); void get_all(std::deque* alerts); @@ -165,6 +166,8 @@ namespace libtorrent { #endif private: + void post_impl(std::auto_ptr& alert_); + std::deque m_alerts; mutable mutex m_mutex; // event m_condition; diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index ab4aaaffc..7ae7386f9 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -1312,6 +1312,16 @@ namespace libtorrent error_code error; }; + struct TORRENT_EXPORT state_update_alert : alert + { + TORRENT_DEFINE_ALERT(state_update_alert); + + const static int static_category = alert::status_notification; + virtual std::string message() const; + virtual bool discardable() const { return false; } + + std::vector status; + }; } diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index dcb5aa3d5..f21562552 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -326,6 +326,7 @@ namespace libtorrent , boost::uint32_t flags) const; void refresh_torrent_status(std::vector* ret , boost::uint32_t flags) const; + void post_torrent_updates(); std::vector get_torrents() const; @@ -1062,6 +1063,10 @@ namespace libtorrent std::vector > m_feeds; + // this is the set of (subscribed) torrents that have changed + // their states since the last time the user requested updates. + std::vector > m_state_updates; + // the main working thread boost::scoped_ptr m_thread; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 17201b298..0cba86b0f 100644 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -184,6 +184,7 @@ namespace libtorrent , boost::uint32_t flags = 0) const; void refresh_torrent_status(std::vector* ret , boost::uint32_t flags = 0) const; + void post_torrent_updates(); // returns a list of all torrents in this session std::vector get_torrents() const; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 67c9a7231..9dc33a83b 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -304,6 +304,10 @@ namespace libtorrent void status(torrent_status* st, boost::uint32_t flags); + // this torrent changed state, if the user is subscribing to + // it, add it to the m_state_updates list in session_impl + void state_updated(); + void file_progress(std::vector& fp, int flags = 0) const; void use_interface(std::string net_interface); @@ -325,6 +329,7 @@ namespace libtorrent if (prio > 255) prio = 255; else if (prio < 0) prio = 0; m_priority = prio; + state_updated(); } #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES @@ -839,6 +844,9 @@ namespace libtorrent void queue_torrent_check(); void dequeue_torrent_check(); + void clear_in_state_update() + { m_in_state_updates = false; } + #ifdef TORRENT_USE_OPENSSL void set_ssl_cert(std::string const& certificate , std::string const& private_key @@ -1322,7 +1330,7 @@ namespace libtorrent // these are the flags sent in on a call to save_resume_data // we need to save them to check them in write_resume_data - boost::uint8_t m_save_resume_flags; + boost::uint8_t m_save_resume_flags; // set to true when this torrent has been paused but // is waiting to finish all current download requests @@ -1359,6 +1367,16 @@ namespace libtorrent // paused it's removed and when it's started again, it's // re-added bool m_in_encrypted_list:1; + + // state subscription. If set, a pointer to this torrent + // will be added to the m_state_updates set in session_impl + // whenever this torrent's state changes (any state). + bool m_state_subscription:1; + + // in state_updates list. When adding a torrent to the + // session_impl's m_state_update list, this bit is set + // to never add the same torrent twice + bool m_in_state_updates:1; #if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS public: diff --git a/src/alert.cpp b/src/alert.cpp index 6b31de3df..0fcb7bec1 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -403,22 +403,20 @@ namespace libtorrent { dispatcher(*alert_); } - void alert_manager::post_alert(const alert& alert_) + void alert_manager::post_alert_ptr(alert* alert_) { - + std::auto_ptr a(alert_); mutex::scoped_lock lock(m_mutex); - if (m_dispatch) - { - TORRENT_ASSERT(m_alerts.empty()); - TORRENT_TRY { - m_dispatch(std::auto_ptr(alert_.clone())); - } TORRENT_CATCH(std::exception&) {} - } - else if (m_alerts.size() < m_queue_size_limit || !alert_.discardable()) - { - m_alerts.push_back(alert_.clone().release()); - } + post_impl(a); + } + + void alert_manager::post_alert(const alert& alert_) + { + std::auto_ptr a(alert_.clone()); + mutex::scoped_lock lock(m_mutex); + + post_impl(a); #ifndef TORRENT_DISABLE_EXTENSIONS lock.unlock(); @@ -433,6 +431,21 @@ namespace libtorrent { #endif } + + void alert_manager::post_impl(std::auto_ptr& alert_) + { + if (m_dispatch) + { + TORRENT_ASSERT(m_alerts.empty()); + TORRENT_TRY { + m_dispatch(alert_); + } TORRENT_CATCH(std::exception&) {} + } + else if (m_alerts.size() < m_queue_size_limit || !alert_->discardable()) + { + m_alerts.push_back(alert_.release()); + } + } #ifndef TORRENT_DISABLE_EXTENSIONS void alert_manager::add_extension(boost::shared_ptr ext) @@ -601,5 +614,12 @@ namespace libtorrent { return msg; } + std::string state_update_alert::message() const + { + char msg[600]; + snprintf(msg, sizeof(msg), "state updates for %d torrents", int(status.size())); + return msg; + } + } // namespace libtorrent diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index a6baa8f1f..6cca18e62 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -2139,11 +2139,15 @@ namespace libtorrent TORRENT_ASSERT(m_outstanding_bytes >= bytes); m_outstanding_bytes -= bytes; if (m_outstanding_bytes < 0) m_outstanding_bytes = 0; -#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS boost::shared_ptr t = associated_torrent().lock(); +#if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS TORRENT_ASSERT(m_received_in_piece + bytes <= t->block_size()); m_received_in_piece += bytes; #endif + + // progress of this torrent increased + t->state_updated(); + #if !defined TORRENT_DISABLE_INVARIANT_CHECKS && defined TORRENT_DEBUG check_invariant(); #endif diff --git a/src/policy.cpp b/src/policy.cpp index 87b54488d..085355dd8 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -939,6 +939,7 @@ namespace libtorrent // this cannot be a connect candidate anymore, since i->connection is set TORRENT_ASSERT(!is_connect_candidate(*i, m_finished)); TORRENT_ASSERT(has_connection(&c)); + m_torrent->state_updated(); return true; } @@ -1091,6 +1092,8 @@ namespace libtorrent if (is_connect_candidate(*p, m_finished)) ++m_num_connect_candidates; + m_torrent->state_updated(); + return true; } @@ -1195,6 +1198,7 @@ namespace libtorrent p = *iter; update_peer(p, src, flags, tcp::endpoint(), destination); } + m_torrent->state_updated(); return p; } #endif // TORRENT_USE_I2P diff --git a/src/session.cpp b/src/session.cpp index 757bdeebe..021f87cb3 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -567,6 +567,11 @@ namespace libtorrent TORRENT_SYNC_CALL2(refresh_torrent_status, ret, flags); } + void session::post_torrent_updates() + { + TORRENT_ASYNC_CALL(post_torrent_updates); + } + std::vector session::get_torrents() const { TORRENT_SYNC_CALL_RET(std::vector, get_torrents); diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 1d5f6277c..b933c844d 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -4342,6 +4342,25 @@ namespace aux { t->status(&*i, flags); } } + + void session_impl::post_torrent_updates() + { + std::auto_ptr alert(new state_update_alert()); + alert->status.reserve(m_state_updates.size()); + + for (std::vector >::iterator i = m_state_updates.begin() + , end(m_state_updates.end()); i != end; ++i) + { + boost::shared_ptr t = i->lock(); + if (!t) continue; + alert->status.push_back(torrent_status()); + t->clear_in_state_update(); + t->status(&alert->status.back(), 0xffffffff); + } + m_state_updates.clear(); + + m_alerts.post_alert_ptr(alert.release()); + } std::vector session_impl::get_torrents() const { @@ -4378,6 +4397,10 @@ namespace aux { { TORRENT_ASSERT(!params.save_path.empty()); +#ifndef TORRENT_NO_DEPRECATE + params.update_flags(); +#endif + if (params.ti && params.ti->is_valid() && params.ti->num_files() == 0) { ec = errors::no_files_in_torrent; diff --git a/src/torrent.cpp b/src/torrent.cpp index 367cbe3da..66b265a97 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -427,6 +427,8 @@ namespace libtorrent , m_apply_ip_filter(p.flags & add_torrent_params::flag_apply_ip_filter) , m_merge_resume_trackers(p.flags & add_torrent_params::flag_merge_resume_trackers) , m_in_encrypted_list(false) + , m_state_subscription(p.flags & add_torrent_params::flag_update_subscribe) + , m_in_state_updates(false) { #if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS m_resume_data_loaded = false; @@ -700,6 +702,8 @@ namespace libtorrent get_handle())); } + state_updated(); + set_state(torrent_status::downloading); m_override_resume_data = true; @@ -855,6 +859,7 @@ namespace libtorrent } m_apply_ip_filter = b; m_policy.ip_filter_updated(); + state_updated(); } #ifndef TORRENT_DISABLE_DHT @@ -1001,6 +1006,7 @@ namespace libtorrent m_upload_mode = b; + state_updated(); send_upload_only(); if (m_upload_mode) @@ -2471,16 +2477,21 @@ ctx->set_verify_callback(verify_function, ec); INVARIANT_CHECK; TORRENT_ASSERT(req.kind == tracker_request::scrape_request); - if (complete >= 0) m_complete = complete; - if (incomplete >= 0) m_incomplete = incomplete; - if (downloaders >= 0) m_downloaders = downloaders; - - if (m_ses.m_alerts.should_post()) - { - m_ses.m_alerts.post_alert(scrape_reply_alert( - get_handle(), m_incomplete, m_complete, req.url)); - } - } + if ((complete >= 0 && m_complete != complete) + || (incomplete >= 0 && m_incomplete != incomplete) + || (downloaders >= 0 && m_downloaders != downloaders)) + state_updated(); + + if (complete >= 0) m_complete = complete; + if (incomplete >= 0) m_incomplete = incomplete; + if (downloaders >= 0) m_downloaders = downloaders; + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert(scrape_reply_alert( + get_handle(), m_incomplete, m_complete, req.url)); + } + } void torrent::tracker_response( tracker_request const& r @@ -2673,6 +2684,8 @@ ctx->set_verify_callback(verify_function, ec); ++m_ses.m_boost_connections; } } + + state_updated(); } ptime torrent::next_announce() const @@ -3061,6 +3074,8 @@ ctx->set_verify_callback(verify_function, ec); TORRENT_ASSERT(!m_picker->have_piece(index)); + state_updated(); + // even though the piece passed the hash-check // it might still have failed being written to disk // if so, piece_picker::write_failed() has been @@ -3176,6 +3191,7 @@ ctx->set_verify_callback(verify_function, ec); , index)); } + state_updated(); m_need_save_resume_data = true; remove_time_critical_piece(index, true); @@ -3593,6 +3609,7 @@ ctx->set_verify_callback(verify_function, ec); write_resume_data(*j.resume_data); alerts().post_alert(save_resume_data_alert(j.resume_data , get_handle())); + state_updated(); } } @@ -3828,6 +3845,8 @@ ctx->set_verify_callback(verify_function, ec); update_peer_interest(was_finished); remove_time_critical_pieces(pieces); } + + state_updated(); } void torrent::piece_priorities(std::vector* pieces) const @@ -4178,6 +4197,7 @@ ctx->set_verify_callback(verify_function, ec); TORRENT_ASSERT(m_num_uploads > 0); if (!c.send_choke()) return false; --m_num_uploads; + state_updated(); return true; } @@ -4194,6 +4214,7 @@ ctx->set_verify_callback(verify_function, ec); if (m_num_uploads >= m_max_uploads && !optimistic) return false; if (!c.send_unchoke()) return false; ++m_num_uploads; + state_updated(); return true; } @@ -5288,7 +5309,6 @@ ctx->set_verify_callback(verify_function, ec); file_priority.clear(); for (int i = 0, end(m_file_priority.size()); i < end; ++i) file_priority.push_back(m_file_priority[i]); - } void torrent::get_full_peer_list(std::vector& v) const @@ -5846,6 +5866,8 @@ ctx->set_verify_callback(verify_function, ec); send_upload_only(); + state_updated(); + m_completed_time = time(0); // disconnect all seeds @@ -6294,7 +6316,10 @@ ctx->set_verify_callback(verify_function, ec); void torrent::set_sequential_download(bool sd) { TORRENT_ASSERT(m_ses.is_network_thread()); + if (m_sequential_download == sd) return; m_sequential_download = sd; + + state_updated(); } void torrent::queue_up() @@ -6317,6 +6342,8 @@ ctx->set_verify_callback(verify_function, ec); if (is_finished() && p != -1) return; if (p == m_sequence_number) return; + state_updated(); + session_impl::torrent_map& torrents = m_ses.m_torrents; if (p >= 0 && m_sequence_number == -1) { @@ -6386,6 +6413,7 @@ ctx->set_verify_callback(verify_function, ec); TORRENT_ASSERT(m_ses.is_network_thread()); TORRENT_ASSERT(limit >= -1); if (limit <= 0) limit = (std::numeric_limits::max)(); + if (m_max_uploads != limit) state_updated(); m_max_uploads = limit; } @@ -6394,6 +6422,7 @@ ctx->set_verify_callback(verify_function, ec); TORRENT_ASSERT(m_ses.is_network_thread()); TORRENT_ASSERT(limit >= -1); if (limit <= 0) limit = (std::numeric_limits::max)(); + if (m_max_connections != limit) state_updated(); m_max_connections = limit; if (num_peers() > int(m_max_connections)) @@ -6446,6 +6475,8 @@ ctx->set_verify_callback(verify_function, ec); TORRENT_ASSERT(m_ses.is_network_thread()); TORRENT_ASSERT(limit >= -1); if (limit <= 0) limit = 0; + if (m_bandwidth_channel[peer_connection::upload_channel].throttle() != limit) + state_updated(); m_bandwidth_channel[peer_connection::upload_channel].throttle(limit); } @@ -6462,6 +6493,8 @@ ctx->set_verify_callback(verify_function, ec); TORRENT_ASSERT(m_ses.is_network_thread()); TORRENT_ASSERT(limit >= -1); if (limit <= 0) limit = 0; + if (m_bandwidth_channel[peer_connection::download_channel].throttle() != limit) + state_updated(); m_bandwidth_channel[peer_connection::download_channel].throttle(limit); } @@ -6502,6 +6535,8 @@ ctx->set_verify_callback(verify_function, ec); m_error = error_code(); m_error_file.clear(); + state_updated(); + // if we haven't downloaded the metadata from m_url, try again if (!m_url.empty() && !m_torrent_file->is_valid()) { @@ -6541,6 +6576,8 @@ ctx->set_verify_callback(verify_function, ec); dequeue_torrent_check(); set_state(torrent_status::queued_for_checking); } + + state_updated(); } void torrent::auto_managed(bool a) @@ -6552,6 +6589,8 @@ ctx->set_verify_callback(verify_function, ec); bool checking_files = should_check_files(); m_auto_managed = a; + state_updated(); + // we need to save this new state as well m_need_save_resume_data = true; @@ -6665,6 +6704,7 @@ ctx->set_verify_callback(verify_function, ec); m_need_save_resume_data = false; m_last_saved_resume = time(0); m_save_resume_flags = boost::uint8_t(flags); + state_updated(); TORRENT_ASSERT(m_storage); if (m_state == torrent_status::queued_for_checking @@ -6734,6 +6774,7 @@ ctx->set_verify_callback(verify_function, ec); // we need to save this new state m_need_save_resume_data = true; + state_updated(); bool prev_graceful = m_graceful_pause_mode; m_graceful_pause_mode = graceful; @@ -6765,6 +6806,8 @@ ctx->set_verify_callback(verify_function, ec); } #endif + state_updated(); + #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING || defined TORRENT_LOGGING log_to_all_peers("PAUSING TORRENT"); #endif @@ -6926,6 +6969,8 @@ ctx->set_verify_callback(verify_function, ec); if (alerts().should_post()) alerts().post_alert(torrent_resumed_alert(get_handle())); + state_updated(); + m_started = time_now(); clear_error(); start_announcing(); @@ -7155,6 +7200,9 @@ ctx->set_verify_callback(verify_function, ec); // let the stats fade out to 0 accumulator += m_stat; m_stat.second_tick(tick_interval_ms); + // if the rate is 0, there's no update because of network transfers + if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) + state_updated(); return; } @@ -7250,6 +7298,10 @@ ctx->set_verify_callback(verify_function, ec); m_total_uploaded += m_stat.last_payload_uploaded(); m_total_downloaded += m_stat.last_payload_downloaded(); m_stat.second_tick(tick_interval_ms); + + // if the rate is 0, there's no update because of network transfers + if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) + state_updated(); } void torrent::recalc_share_mode() @@ -7697,6 +7749,8 @@ ctx->set_verify_callback(verify_function, ec); TORRENT_ASSERT(m_ses.is_network_thread()); peer_id id(0); m_policy.add_peer(adr, id, source, 0); + + state_updated(); } void torrent::async_verify_piece(int piece_index, boost::function const& f) @@ -7944,6 +7998,8 @@ ctx->set_verify_callback(verify_function, ec); m_ses.m_alerts.post_alert(state_changed_alert(get_handle(), s, (torrent_status::state_t)m_state)); m_state = s; + state_updated(); + #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) @@ -7969,6 +8025,17 @@ ctx->set_verify_callback(verify_function, ec); } #endif + void torrent::state_updated() + { + // we're either not subscribing to this torrent, or + // it has already been updated this round, no need to + // add it to the list twice + if (!m_state_subscription || m_in_state_updates) return; + + m_ses.m_state_updates.push_back(shared_from_this()); + m_in_state_updates = true; + } + void torrent::status(torrent_status* st, boost::uint32_t flags) { INVARIANT_CHECK;