diff --git a/ChangeLog b/ChangeLog index 9bde90910..18e90249a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ * deprecate set_ratio(), and per-peer rate limits * add web seed support for torrents with pad files - * introduced a more scalable API for torrent status updates (post_torrent_updates()) + * introduced a more scalable API for torrent status updates (post_torrent_updates()) and updated client_test to use it * 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/examples/client_test.cpp b/examples/client_test.cpp index 9bd1f21f6..bcf787c74 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include +#include #ifdef _MSC_VER #pragma warning(pop) @@ -277,26 +278,41 @@ bool show_torrent(libtorrent::torrent_status const& st, int torrent_filter, int* bool yes(libtorrent::torrent_status const&) { return true; } -bool compare_torrent(torrent_status const& lhs, torrent_status const& rhs) -{ - if (lhs.queue_position != -1 && rhs.queue_position != -1) - { - // both are downloading, sort by queue pos - return lhs.queue_position < rhs.queue_position; - } - else if (lhs.queue_position == -1 && rhs.queue_position == -1) - { - // both are seeding, sort by seed-rank - return lhs.seed_rank > rhs.seed_rank; - } - - return (lhs.queue_position == -1) < (rhs.queue_position == -1); -} - FILE* g_log_file = 0; int active_torrent = 0; +bool compare_torrent(torrent_status const* lhs, torrent_status const* rhs) +{ + if (lhs->queue_position != -1 && rhs->queue_position != -1) + { + // both are downloading, sort by queue pos + return lhs->queue_position < rhs->queue_position; + } + else if (lhs->queue_position == -1 && rhs->queue_position == -1) + { + // both are seeding, sort by seed-rank + return lhs->seed_rank > rhs->seed_rank; + } + + return (lhs->queue_position == -1) < (rhs->queue_position == -1); +} + +void update_filtered_torrents(boost::unordered_set& all_handles + , std::vector& filtered_handles, int* counters) +{ + filtered_handles.clear(); + memset(counters, 0, sizeof(int) * torrents_max); + for (boost::unordered_set::iterator i = all_handles.begin() + , end(all_handles.end()); i != end; ++i) + { + if (!show_torrent(*i, torrent_filter, counters)) continue; + filtered_handles.push_back(&*i); + } + if (active_torrent >= int(filtered_handles.size())) active_torrent = filtered_handles.size() - 1; + std::sort(filtered_handles.begin(), filtered_handles.end(), &compare_torrent); +} + char const* esc(char const* code) { #ifdef ANSI_TERMINAL_COLORS @@ -785,13 +801,11 @@ void scan_dir(std::string const& dir_path } } -torrent_status const& get_active_torrent(std::vector const& torrents) +torrent_status const& get_active_torrent(std::vector const& filtered_handles) { - if (active_torrent >= int(torrents.size()) + if (active_torrent >= int(filtered_handles.size()) || active_torrent < 0) active_torrent = 0; - std::vector::const_iterator i = torrents.begin(); - std::advance(i, active_torrent); - return *i; + return *filtered_handles[active_torrent]; } void print_alert(libtorrent::alert const* a, std::string& str) @@ -835,8 +849,13 @@ int save_file(std::string const& filename, std::vector& v) return 0; } -void handle_alert(libtorrent::session& ses, libtorrent::alert* a - , handles_t& files, std::set const& non_files) +// returns true if the alert was handled (and should not be printed to the log) +// returns false if the alert was not handled +bool handle_alert(libtorrent::session& ses, libtorrent::alert* a + , handles_t& files, std::set const& non_files + , int* counters, boost::unordered_set& all_handles + , std::vector& filtered_handles + , bool& need_resort) { using namespace libtorrent; @@ -854,7 +873,7 @@ void handle_alert(libtorrent::session& ses, libtorrent::alert* a char msg[256]; snprintf(msg, sizeof(msg), "ERROR. could not load certificate %s: %s\n", cert.c_str(), ec.message().c_str()); if (g_log_file) fprintf(g_log_file, "[%s] %s\n", time_now_string(), msg); - return; + return true; } stat_file(priv, &st, ec); if (ec) @@ -862,7 +881,7 @@ void handle_alert(libtorrent::session& ses, libtorrent::alert* a char msg[256]; snprintf(msg, sizeof(msg), "ERROR. could not load private key %s: %s\n", priv.c_str(), ec.message().c_str()); if (g_log_file) fprintf(g_log_file, "[%s] %s\n", time_now_string(), msg); - return; + return true; } char msg[256]; @@ -897,6 +916,14 @@ void handle_alert(libtorrent::session& ses, libtorrent::alert* a #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES h.resolve_countries(true); #endif + + boost::unordered_set::iterator j + = all_handles.insert(h.status()).first; + if (show_torrent(*j, torrent_filter, counters)) + { + filtered_handles.push_back(&*j); + need_resort = true; + } } } else if (torrent_finished_alert* p = alert_cast(a)) @@ -946,6 +973,21 @@ void handle_alert(libtorrent::session& ses, libtorrent::alert* a h.save_resume_data(); ++num_outstanding_resume_data; } + else if (state_update_alert* p = alert_cast(a)) + { + for (std::vector::iterator i = p->status.begin(); + i != p->status.end(); ++i) + { + boost::unordered_set::iterator j = all_handles.find(*i); + // don't add new entries here, that's done in the handler + // for add_torrent_alert + if (j == all_handles.end()) continue; + ((torrent_status&)*j) = *i; + } + + return true; + } + return false; } void print_piece(libtorrent::partial_piece_info* pp @@ -1120,7 +1162,9 @@ int main(int argc, char* argv[]) // it was added through the directory monitor. It is used to // be able to remove torrents that were added via the directory // monitor when they're not in the directory anymore. - std::vector handles; + boost::unordered_set all_handles; + std::vector filtered_handles; + handles_t files; // torrents that were not added via the monitor dir std::set non_files; @@ -1447,16 +1491,15 @@ int main(int argc, char* argv[]) while (loop_limit > 1 || loop_limit == 0) { ++tick; - handles.clear(); - memset(counters, 0, sizeof(counters)); - ses.get_torrent_status(&handles, boost::bind(&show_torrent, _1, torrent_filter, (int*)counters)); - if (active_torrent >= int(handles.size())) active_torrent = handles.size() - 1; - else if (active_torrent >= 0) + ses.post_torrent_updates(); + if (active_torrent >= int(filtered_handles.size())) active_torrent = filtered_handles.size() - 1; + if (active_torrent >= 0) { // ask for distributed copies for the selected torrent. Since this // is a somewhat expensive operation, don't do it by default for // all torrents - handles[active_torrent] = handles[active_torrent].handle.status( + torrent_status const& h = *filtered_handles[active_torrent]; + h.handle.status( torrent_handle::query_distributed_copies | torrent_handle::query_pieces | torrent_handle::query_verified_pieces); @@ -1467,7 +1510,7 @@ int main(int argc, char* argv[]) counters[torrents_feeds] = feeds.size(); - std::sort(handles.begin(), handles.end(), &compare_torrent); + std::sort(filtered_handles.begin(), filtered_handles.end(), &compare_torrent); if (loop_limit > 1) --loop_limit; int c = 0; @@ -1497,11 +1540,7 @@ int main(int argc, char* argv[]) if (torrent_filter > 0) { --torrent_filter; - handles.clear(); - memset(counters, 0, sizeof(counters)); - ses.get_torrent_status(&handles, boost::bind(&show_torrent, _1, torrent_filter, (int*)counters)); - if (active_torrent >= int(handles.size())) active_torrent = handles.size() - 1; - std::sort(handles.begin(), handles.end(), &compare_torrent); + update_filtered_torrents(all_handles, filtered_handles, counters); } } else if (c == 67) @@ -1510,11 +1549,7 @@ int main(int argc, char* argv[]) if (torrent_filter < torrents_max - 1) { ++torrent_filter; - handles.clear(); - memset(counters, 0, sizeof(counters)); - ses.get_torrent_status(&handles, boost::bind(&show_torrent, _1, torrent_filter, (int*)counters)); - if (active_torrent >= int(handles.size())) active_torrent = handles.size() - 1; - std::sort(handles.begin(), handles.end(), &compare_torrent); + update_filtered_torrents(all_handles, filtered_handles, counters); } } else if (c == 65) @@ -1527,7 +1562,8 @@ int main(int argc, char* argv[]) { // arrow down ++active_torrent; - if (active_torrent >= int(handles.size())) active_torrent = handles.size() - 1; + if (active_torrent >= int(filtered_handles.size())) + active_torrent = filtered_handles.size() - 1; } } @@ -1567,7 +1603,7 @@ int main(int argc, char* argv[]) if (c == 'D') { - torrent_handle h = get_active_torrent(handles).handle; + torrent_handle h = get_active_torrent(filtered_handles).handle; if (h.is_valid()) { printf("\n\nARE YOU SURE YOU WANT TO DELETE THE FILES FOR '%s'. THIS OPERATION CANNOT BE UNDONE. (y/N)" @@ -1592,39 +1628,39 @@ int main(int argc, char* argv[]) } } - if (c == 'j' && !handles.empty()) + if (c == 'j' && !filtered_handles.empty()) { - get_active_torrent(handles).handle.force_recheck(); + get_active_torrent(filtered_handles).handle.force_recheck(); } - if (c == 'r' && !handles.empty()) + if (c == 'r' && !filtered_handles.empty()) { - get_active_torrent(handles).handle.force_reannounce(); + get_active_torrent(filtered_handles).handle.force_reannounce(); } - if (c == 's' && !handles.empty()) + if (c == 's' && !filtered_handles.empty()) { - torrent_status const& ts = get_active_torrent(handles); + torrent_status const& ts = get_active_torrent(filtered_handles); ts.handle.set_sequential_download(!ts.sequential_download); } if (c == 'R') { // save resume data for all torrents - for (std::vector::iterator i = handles.begin() - , end(handles.end()); i != end; ++i) + for (std::vector::iterator i = filtered_handles.begin() + , end(filtered_handles.end()); i != end; ++i) { - if (i->need_save_resume) + if ((*i)->need_save_resume) { - i->handle.save_resume_data(); + (*i)->handle.save_resume_data(); ++num_outstanding_resume_data; } } } - if (c == 'o' && !handles.empty()) + if (c == 'o' && !filtered_handles.empty()) { - torrent_status const& ts = get_active_torrent(handles); + torrent_status const& ts = get_active_torrent(filtered_handles); int num_pieces = ts.num_pieces; if (num_pieces > 300) num_pieces = 300; for (int i = 0; i < num_pieces; ++i) @@ -1633,15 +1669,15 @@ int main(int argc, char* argv[]) } } - if (c == 'v' && !handles.empty()) + if (c == 'v' && !filtered_handles.empty()) { - torrent_status const& ts = get_active_torrent(handles); + torrent_status const& ts = get_active_torrent(filtered_handles); ts.handle.scrape_tracker(); } - if (c == 'p' && !handles.empty()) + if (c == 'p' && !filtered_handles.empty()) { - torrent_status const& ts = get_active_torrent(handles); + torrent_status const& ts = get_active_torrent(filtered_handles); if (!ts.auto_managed && ts.paused) { ts.handle.auto_managed(true); @@ -1654,16 +1690,16 @@ int main(int argc, char* argv[]) } // toggle force-start - if (c == 'k' && !handles.empty()) + if (c == 'k' && !filtered_handles.empty()) { - torrent_status const& ts = get_active_torrent(handles); + torrent_status const& ts = get_active_torrent(filtered_handles); ts.handle.auto_managed(!ts.auto_managed); if (ts.auto_managed && ts.paused) ts.handle.resume(); } - if (c == 'c' && !handles.empty()) + if (c == 'c' && !filtered_handles.empty()) { - torrent_status const& ts = get_active_torrent(handles); + torrent_status const& ts = get_active_torrent(filtered_handles); ts.handle.clear_error(); } @@ -1731,16 +1767,26 @@ int main(int argc, char* argv[]) for (std::deque::iterator i = alerts.begin() , end(alerts.end()); i != end; ++i) { - std::string event_string; - - ::print_alert(*i, event_string); + bool need_resort = false; TORRENT_TRY { - ::handle_alert(ses, *i, files, non_files); + if (!::handle_alert(ses, *i, files, non_files, counters + , all_handles, filtered_handles, need_resort)) + { + // if we didn't handle the alert, print it to the log + std::string event_string; + print_alert(*i, event_string); + events.push_back(event_string); + if (events.size() >= 20) events.pop_front(); + } } TORRENT_CATCH(std::exception& e) {} - events.push_back(event_string); - if (events.size() >= 20) events.pop_front(); + if (need_resort) + { + std::sort(filtered_handles.begin(), filtered_handles.end() + , &compare_torrent); + } + delete *i; } alerts.clear(); @@ -1794,8 +1840,8 @@ int main(int argc, char* argv[]) } } - for (std::vector::iterator i = handles.begin(); - i != handles.end(); ++torrent_index) + for (std::vector::iterator i = filtered_handles.begin(); + i != filtered_handles.end(); ++torrent_index) { if (lines_printed >= terminal_height - 15) { @@ -1803,10 +1849,10 @@ int main(int argc, char* argv[]) break; } - torrent_status& s = *i; + torrent_status const& s = **i; if (!s.handle.is_valid()) { - i = handles.erase(i); + i = filtered_handles.erase(i); continue; } else @@ -2037,7 +2083,7 @@ int main(int argc, char* argv[]) } torrent_status const* st = 0; - if (!handles.empty()) st = &get_active_torrent(handles); + if (!filtered_handles.empty()) st = &get_active_torrent(filtered_handles); if (st && st->handle.is_valid()) { torrent_handle h = st->handle; diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 578f575f6..6752ab19b 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -71,6 +71,8 @@ namespace libtorrent struct peer_list_entry; struct torrent_status; + std::size_t hash_value(torrent_status const& ts); + #ifndef BOOST_NO_EXCEPTIONS // for compatibility with 0.14 typedef libtorrent_exception duplicate_torrent; @@ -155,6 +157,7 @@ namespace libtorrent friend struct aux::session_impl; friend struct feed; friend class torrent; + friend std::size_t hash_value(torrent_handle const& th); torrent_handle() {} @@ -496,6 +499,9 @@ namespace libtorrent , listen_port(0) {} + bool operator==(torrent_status const& st) const + { return handle == st.handle; } + // handle to the torrent torrent_handle handle; diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index 42be22823..e8c2516c6 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -899,5 +899,14 @@ namespace libtorrent TORRENT_ASYNC_CALL1(reset_piece_deadline, index); } + std::size_t hash_value(torrent_status const& ts) + { + return hash_value(ts.handle); + } + + std::size_t hash_value(torrent_handle const& th) + { + return std::size_t(th.m_torrent.lock().get()); + } }