add stop-when-ready feature, to support checking torrents without starting the download afterwards

This commit is contained in:
arvidn 2015-10-03 18:15:29 -04:00 committed by arvidn
parent 5c96a35a66
commit 51c6079c42
11 changed files with 144 additions and 5 deletions

View File

@ -1,3 +1,4 @@
* add feature to stop torrents immediately after checking files is done
* make all non-auto managed torrents exempt from queuing logic, including
checking torrents.
* add option to not proxy tracker connections through proxy

View File

@ -616,6 +616,7 @@ void bind_session()
.value("flag_sequential_download", add_torrent_params::flag_sequential_download)
.value("flag_use_resume_save_path", add_torrent_params::flag_use_resume_save_path)
.value("flag_merge_resume_http_seeds", add_torrent_params::flag_merge_resume_http_seeds)
.value("flag_stop_when_ready", add_torrent_params::flag_stop_when_ready)
;
class_<cache_status>("cache_status")
#ifndef TORRENT_NO_DEPRECATE

View File

@ -417,6 +417,7 @@ void bind_torrent_handle()
.def("is_valid", _(&torrent_handle::is_valid))
.def("pause", _(&torrent_handle::pause), arg("flags") = 0)
.def("resume", _(&torrent_handle::resume))
.def("stop_when_ready", _(&torrent_handle::stop_when_ready))
.def("clear_error", _(&torrent_handle::clear_error))
.def("set_priority", _(&torrent_handle::set_priority))
.def("super_seeding", super_seeding1)

View File

@ -28,6 +28,7 @@ void bind_torrent_status()
.def_readonly("info_hash", &torrent_status::info_hash)
.def_readonly("state", &torrent_status::state)
.def_readonly("paused", &torrent_status::paused)
.def_readonly("stop_when_ready", &torrent_status::stop_when_ready)
.def_readonly("auto_managed", &torrent_status::auto_managed)
.def_readonly("sequential_download", &torrent_status::sequential_download)
.def_readonly("is_seeding", &torrent_status::is_seeding)

View File

@ -267,6 +267,11 @@ namespace libtorrent
// is passed in here as well as the .torrent file.
flag_merge_resume_http_seeds = 0x2000,
// the stop when ready flag. Setting this flag is equivalent to calling
// torrent_handle::stop_when_ready() immediately after the torrent is
// added.
flag_stop_when_ready = 0x4000,
// internal
default_flags = flag_pinned | flag_update_subscribe
| flag_auto_managed | flag_paused | flag_apply_ip_filter

View File

@ -478,6 +478,8 @@ namespace libtorrent
void set_announce_to_trackers(bool b) { m_announce_to_trackers = b; }
void set_announce_to_lsd(bool b) { m_announce_to_lsd = b; }
void stop_when_ready(bool b) { m_stop_when_ready = b; }
int started() const { return m_started; }
void step_session_time(int seconds);
void do_pause();
@ -1700,6 +1702,10 @@ namespace libtorrent
// file
bool m_merge_resume_http_seeds:1;
// if this is set, whenever transitioning into a downloading/seeding state
// from a non-downloading/seeding state, the torrent is paused.
bool m_stop_when_ready:1;
#if TORRENT_USE_ASSERTS
public:
// set to false until we've loaded resume data

View File

@ -554,6 +554,19 @@ namespace libtorrent
void pause(int flags = 0) const;
void resume() const;
// set or clear the stop-when-ready flag. When this flag is set, the
// torrent will *force stop* whenever it transitions from a
// non-data-transferring state into a data-transferring state (referred to
// as being ready to download or seed). This is useful for torrents that
// should not start downloading or seeding yet, but what to be made ready
// to do so. A torrent may need to have its files checked for instance, so
// it needs to be started and possibly queued for checking (auto-managed
// and started) but as soon as it's done, it should be stopped.
//
// *Force stopped* means auto-managed is set to false and it's paused. As
// if auto_manage(false) and pause() were called on the torrent.
void stop_when_ready(bool b) const;
// Explicitly sets the upload mode of the torrent. In upload mode, the
// torrent will not request any pieces. If the torrent is auto managed,
// it will automatically be taken out of upload mode periodically (see

View File

@ -485,6 +485,11 @@ namespace libtorrent
bool announcing_to_lsd;
bool announcing_to_dht;
// this reflects whether the ``stop_when_ready`` flag is currently enabled
// on this torrent. For more information, see
// torrent_handle::stop_when_ready().
bool stop_when_ready;
// the info-hash for this torrent
sha1_hash info_hash;
};

View File

@ -349,7 +349,6 @@ TORRENT_TEST(seed_limit)
},
[](lt::session& ses) {
// verify result (none should have been started)
// make sure only 3 got started
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
@ -439,7 +438,6 @@ TORRENT_TEST(download_limit)
},
[](lt::session& ses) {
// verify result (none should have been started)
// make sure only 3 got started
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
@ -538,7 +536,6 @@ TORRENT_TEST(checking_announce)
},
[](lt::session& ses) {
// verify result (none should have been started)
// make sure only 3 got started
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
@ -590,7 +587,6 @@ TORRENT_TEST(paused_checking)
},
[](lt::session& ses) {
// verify result (none should have been started)
// make sure only 3 got started
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
@ -619,6 +615,71 @@ TORRENT_TEST(paused_checking)
}
});
}
// set the stop_when_ready flag and make sure we receive a paused alert *before*
// a state_changed_alert
TORRENT_TEST(stop_when_ready)
{
run_test(
[](settings_pack& sett) {},
[](lt::session& ses) {
// add torrents
lt::add_torrent_params params = create_torrent(0, true);
// torrents are started and auto-managed
params.flags |= add_torrent_params::flag_auto_managed;
params.flags |= add_torrent_params::flag_stop_when_ready;
// we need this to get the tracker_announce_alert
params.trackers.push_back("http://10.10.0.2/announce");
ses.async_add_torrent(params);
},
[](lt::session& ses) {
// verify result (none should have been started)
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
lt::time_point start_time = alerts[0]->timestamp();
int num_paused = 0;
for (alert* a : alerts)
{
fprintf(stderr, "%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
- start_time).count()), a->message().c_str());
if (alert_cast<torrent_paused_alert>(a))
{
++num_paused;
}
if (state_changed_alert* sc = alert_cast<state_changed_alert>(a))
{
if (sc->state == torrent_status::seeding)
{
// once we turn into beeing a seed. we should have been paused
// already.
TEST_EQUAL(num_paused, 1);
}
}
// there should not have been any announces. the torrent should have
// been stopped *before* announcing.
TEST_EQUAL(alert_cast<tracker_announce_alert>(a), NULL);
}
for (torrent_handle const& h : ses.get_torrents())
{
// the torrent should have been force-stopped (after checking was
// donw, because we set the stop_when_ready flag). Force stopped
// means not auto-managed and paused.
torrent_status st = h.status();
TEST_CHECK(!st.auto_managed);
TEST_EQUAL(st.paused, true);
// it should be seeding. If it's not seeding it may not have had its
// files checked.
TEST_EQUAL(st.state, torrent_status::seeding);
}
});
}
// TODO: assert that the torrent_paused_alert is posted when pausing
// downloading, seeding, checking torrents as well as the graceful pause
// TODO: test limits of tracker, DHT and LSD announces

View File

@ -279,6 +279,7 @@ namespace libtorrent
, m_pending_active_change(false)
, m_use_resume_save_path(p.flags & add_torrent_params::flag_use_resume_save_path)
, m_merge_resume_http_seeds(p.flags & add_torrent_params::flag_merge_resume_http_seeds)
, m_stop_when_ready(p.flags & add_torrent_params::flag_stop_when_ready)
{
// we cannot log in the constructor, because it relies on shared_from_this
// being initialized, which happens after the constructor returns.
@ -11610,6 +11611,29 @@ namespace libtorrent
if (m_peer_list) m_peer_list->clear_peer_prio();
}
namespace
{
bool is_downloading_state(int st)
{
switch (st)
{
case torrent_status::checking_files:
case torrent_status::allocating:
case torrent_status::checking_resume_data:
return false;
case torrent_status::downloading_metadata:
case torrent_status::downloading:
case torrent_status::finished:
case torrent_status::seeding:
return true;
default:
// unexpected state
TORRENT_ASSERT_VAL(false, st);
return false;
}
}
}
void torrent::set_state(torrent_status::state_t s)
{
TORRENT_ASSERT(is_single_thread());
@ -11643,6 +11667,21 @@ namespace libtorrent
get_handle());
}
if (m_stop_when_ready
&& !is_downloading_state(m_state)
&& is_downloading_state(s))
{
#ifndef TORRENT_DISABLE_LOGGING
debug_log("stop_when_ready triggered");
#endif
// stop_when_ready is set, and we're transitioning from a downloading
// state to a non-downloading state. pause the torrent. Note that
// "downloading" is defined broadly to include any state where we
// either upload or download (for the purpose of this flag).
auto_managed(false);
pause();
}
m_state = s;
#ifndef TORRENT_DISABLE_LOGGING
@ -11742,6 +11781,7 @@ namespace libtorrent
st->announcing_to_trackers = m_announce_to_trackers;
st->announcing_to_lsd = m_announce_to_lsd;
st->announcing_to_dht = m_announce_to_dht;
st->stop_when_ready = m_stop_when_ready;
st->added_time = m_added_time;
st->completed_time = m_completed_time;

View File

@ -329,6 +329,11 @@ namespace libtorrent
TORRENT_ASYNC_CALL1(pause, bool(flags & graceful_pause));
}
void torrent_handle::stop_when_ready(bool b) const
{
TORRENT_ASYNC_CALL1(stop_when_ready, b);
}
void torrent_handle::apply_ip_filter(bool b) const
{
TORRENT_ASYNC_CALL1(set_apply_ip_filter, b);