From 723aba6c24bd9ebd8110c153c2ef957471efe4ca Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Wed, 19 Nov 2008 00:46:48 +0000 Subject: [PATCH] solved issue with pausing checking torrents. Introduced a new state, checkin_resume_data, which a torrent is in while waiting for the disk io thread to verify the resume data, but before it has been determined wheter it should be queued for checking or not --- ChangeLog | 5 +++ bindings/python/client.py | 3 +- docs/manual.rst | 8 +++- examples/client_test.cpp | 2 +- include/libtorrent/torrent_handle.hpp | 5 ++- src/session_impl.cpp | 19 +++++++-- src/torrent.cpp | 59 +++++++++++++++++++++------ 7 files changed, 79 insertions(+), 22 deletions(-) diff --git a/ChangeLog b/ChangeLog index 65e1ec100..a8823c5e7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,9 @@ + * torrents that are checking can now be paused, which will + pause the checking + * introduced another torrent state, checking_resume_data, which + the torrent is in when it's first added, and is comparing + the files on disk with the resume data * DHT bandwidth usage optimizations * rate limited DHT send socket * tracker connections are now also subject to IP filtering diff --git a/bindings/python/client.py b/bindings/python/client.py index 56c0e9d30..a3de4198e 100755 --- a/bindings/python/client.py +++ b/bindings/python/client.py @@ -283,7 +283,8 @@ def main(): if s.state != lt.torrent_status.seeding: state_str = ['queued', 'checking', 'downloading metadata', \ - 'downloading', 'finished', 'seeding', 'allocating'] + 'downloading', 'finished', 'seeding', \ + 'allocating', 'checking fastresume'] out += state_str[s.state] + ' ' out += '%5.4f%% ' % (s.progress*100) diff --git a/docs/manual.rst b/docs/manual.rst index d4573abac..beb2f1843 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -2376,7 +2376,8 @@ It contains the following fields:: downloading, finished, seeding, - allocating + allocating, + checking_resume_data }; state_t state; @@ -2453,6 +2454,11 @@ It contains the following fields:: torrent's current task. It may be checking files or downloading. The torrent's current task is in the ``state`` member, it will be one of the following: ++--------------------------+----------------------------------------------------------+ +|``checking_resume_data`` |The torrent is currently checking the fastresume data and | +| |comparing it to the files on disk. This is typically | +| |completed in a fraction of a second, but if you add a | +| |large number of torrents at once, they will queue up. | +--------------------------+----------------------------------------------------------+ |``queued_for_checking`` |The torrent is in the queue for being checked. But there | | |currently is another torrent that are being checked. | diff --git a/examples/client_test.cpp b/examples/client_test.cpp index aade3c33e..016ec39fc 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -667,7 +667,7 @@ void handle_alert(libtorrent::session& ses, libtorrent::alert* a static char const* state_str[] = {"checking (q)", "checking", "dl metadata" - , "downloading", "finished", "seeding", "allocating"}; + , "downloading", "finished", "seeding", "allocating", "checking (r)"}; int main(int ac, char* av[]) { diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index 84a3785f7..8cb97b7cb 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -81,7 +81,7 @@ namespace libtorrent struct TORRENT_EXPORT torrent_status { torrent_status() - : state(queued_for_checking) + : state(checking_resume_data) , paused(false) , progress(0.f) , total_download(0) @@ -130,7 +130,8 @@ namespace libtorrent downloading, finished, seeding, - allocating + allocating, + checking_resume_data }; state_t state; diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 092fd0102..5af255fed 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1875,10 +1875,10 @@ namespace aux { void session_impl::check_torrent(boost::shared_ptr const& t) { if (m_abort) return; - TORRENT_ASSERT(!t->is_paused() || t->is_auto_managed()); - TORRENT_ASSERT(t->state() == torrent_status::checking_files - || t->state() == torrent_status::queued_for_checking); + TORRENT_ASSERT(t->should_check_files()); + TORRENT_ASSERT(t->state() != torrent_status::checking_files); if (m_queued_for_checking.empty()) t->start_checking(); + else t->set_state(torrent_status::queued_for_checking); TORRENT_ASSERT(std::find(m_queued_for_checking.begin() , m_queued_for_checking.end(), t) == m_queued_for_checking.end()); m_queued_for_checking.push_back(t); @@ -1900,17 +1900,21 @@ namespace aux { void session_impl::done_checking(boost::shared_ptr const& t) { + INVARIANT_CHECK; + if (m_queued_for_checking.empty()) return; check_queue_t::iterator next_check = m_queued_for_checking.begin(); check_queue_t::iterator done = m_queued_for_checking.end(); for (check_queue_t::iterator i = m_queued_for_checking.begin() , end(m_queued_for_checking.end()); i != end; ++i) { + TORRENT_ASSERT(*i == t || (*i)->should_check_files()); if (*i == t) done = i; if (next_check == done || (*next_check)->queue_position() > (*i)->queue_position()) next_check = i; } - if (next_check != done) (*next_check)->start_checking(); + // only start a new one if we removed the one that is checking + if (next_check != done && t->state() == torrent_status::checking_files) (*next_check)->start_checking(); m_queued_for_checking.erase(done); } @@ -2631,6 +2635,13 @@ namespace aux { #ifndef NDEBUG void session_impl::check_invariant() const { + int num_checking = std::count_if(m_queued_for_checking.begin() + , m_queued_for_checking.end(), boost::bind(&torrent::state, _1) + == torrent_status::checking_files); + + // the queue is either empty, or it has exactly one checking torrent in it + TORRENT_ASSERT(m_queued_for_checking.empty() || num_checking == 1); + std::set unique; int total_downloaders = 0; for (torrent_map::const_iterator i = m_torrents.begin() diff --git a/src/torrent.cpp b/src/torrent.cpp index 52d2b61cb..65605000c 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -170,7 +170,7 @@ namespace libtorrent , m_net_interface(net_interface.address(), 0) , m_save_path(complete(save_path)) , m_storage_mode(storage_mode) - , m_state(torrent_status::queued_for_checking) + , m_state(torrent_status::checking_resume_data) , m_settings(ses.settings()) , m_storage_constructor(sc) , m_progress(0.f) @@ -250,7 +250,7 @@ namespace libtorrent , m_net_interface(net_interface.address(), 0) , m_save_path(complete(save_path)) , m_storage_mode(storage_mode) - , m_state(torrent_status::queued_for_checking) + , m_state(torrent_status::checking_resume_data) , m_settings(ses.settings()) , m_storage_constructor(sc) , m_progress(0.f) @@ -472,7 +472,7 @@ namespace libtorrent std::copy(url_seeds.begin(), url_seeds.end(), std::inserter(m_web_seeds , m_web_seeds.begin())); - set_state(torrent_status::queued_for_checking); + set_state(torrent_status::checking_resume_data); if (m_resume_entry.type() == lazy_entry::dict_t) { @@ -552,6 +552,7 @@ namespace libtorrent } m_error = j.str; pause(); + set_state(torrent_status::queued_for_checking); std::vector().swap(m_resume_data); lazy_entry().swap(m_resume_entry); @@ -680,7 +681,8 @@ namespace libtorrent { // either the fastresume data was rejected or there are // some files - if (!is_torrent_paused() || is_auto_managed()) + set_state(torrent_status::queued_for_checking); + if (should_check_files()) m_ses.check_torrent(shared_from_this()); } @@ -690,10 +692,21 @@ namespace libtorrent void torrent::force_recheck() { - if (m_state == torrent_status::checking_files - || m_state == torrent_status::queued_for_checking) + // if the torrent is already queued to check its files + // don't do anything + if (should_check_files() + || m_state == torrent_status::checking_resume_data) return; + if ((is_paused() && !m_auto_managed)) + { + // set the queued for checking state, so that it's + // checked as soon as it's resumed or made auto managed + set_state(torrent_status::queued_for_checking); + return; + } + if (!m_error.empty()) m_error.clear(); + disconnect_all(); m_owning_storage->async_release_files(); @@ -706,7 +719,7 @@ namespace libtorrent , int((m_torrent_file->total_size()+m_block_size-1)/m_block_size)); // assume that we don't have anything m_files_checked = false; - set_state(torrent_status::queued_for_checking); + set_state(torrent_status::checking_resume_data); if (m_auto_managed) set_queue_position((std::numeric_limits::max)()); @@ -744,6 +757,7 @@ namespace libtorrent void torrent::start_checking() { + TORRENT_ASSERT(should_check_files()); set_state(torrent_status::checking_files); m_storage->async_check_files(bind( @@ -773,7 +787,11 @@ namespace libtorrent } m_error = j.str; pause(); - if (!m_abort) m_ses.done_checking(shared_from_this()); + if (!m_abort) + { + m_ses.done_checking(shared_from_this()); + set_state(torrent_status::queued_for_checking); + } return; } @@ -1636,7 +1654,10 @@ namespace libtorrent } if (m_state == torrent_status::checking_files) + { m_ses.done_checking(shared_from_this()); + set_state(torrent_status::queued_for_checking); + } m_owning_storage = 0; m_host_resolver.cancel(); @@ -3065,7 +3086,8 @@ namespace libtorrent m_has_incoming = true; if ((m_state == torrent_status::queued_for_checking - || m_state == torrent_status::checking_files) + || m_state == torrent_status::checking_files + || m_state == torrent_status::checking_resume_data) && valid_metadata()) { p->disconnect("torrent is not ready to accept peers"); @@ -3133,8 +3155,9 @@ namespace libtorrent { return int(m_connections.size()) < m_max_connections && !is_paused() - && m_state != torrent_status::checking_files - && (m_state != torrent_status::queued_for_checking + && ((m_state != torrent_status::checking_files + && m_state != torrent_status::checking_resume_data + && m_state != torrent_status::queued_for_checking) || !valid_metadata()) && m_policy.num_connect_candidates() > 0 && !m_abort; @@ -3600,6 +3623,9 @@ namespace libtorrent if (is_paused()) TORRENT_ASSERT(num_peers() == 0); + if (!should_check_files()) + TORRENT_ASSERT(m_state != torrent_status::checking_files); + if (!m_ses.m_queued_for_checking.empty()) { // if there are torrents waiting to be checked @@ -3891,9 +3917,12 @@ namespace libtorrent void torrent::clear_error() { if (m_error.empty()) return; + bool checking_files = should_check_files(); if (m_ses.m_auto_manage_time_scaler > 2) m_ses.m_auto_manage_time_scaler = 2; m_error.clear(); + if (!checking_files && should_check_files()) + m_ses.check_torrent(shared_from_this()); } void torrent::auto_managed(bool a) @@ -3914,6 +3943,7 @@ namespace libtorrent // stop checking m_storage->abort_disk_io(); m_ses.done_checking(shared_from_this()); + set_state(torrent_status::queued_for_checking); } } @@ -3994,7 +4024,8 @@ namespace libtorrent TORRENT_ASSERT(m_storage); if (m_state == torrent_status::queued_for_checking - || m_state == torrent_status::checking_files) + || m_state == torrent_status::checking_files + || m_state == torrent_status::checking_resume_data) { if (alerts().should_post()) { @@ -4011,7 +4042,8 @@ namespace libtorrent { return (m_state == torrent_status::checking_files || m_state == torrent_status::queued_for_checking) - && (!is_paused() || m_auto_managed); + && (!is_paused() || m_auto_managed) + && m_error.empty(); } bool torrent::is_paused() const @@ -4033,6 +4065,7 @@ namespace libtorrent // stop checking m_storage->abort_disk_io(); m_ses.done_checking(shared_from_this()); + set_state(torrent_status::queued_for_checking); } }