polish the stop-when-ready flag and improve documentation

This commit is contained in:
arvidn 2015-12-13 11:26:41 -05:00
parent e0959605b9
commit 1712a8e4b4
5 changed files with 239 additions and 2 deletions

View File

@ -464,7 +464,7 @@ 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; }
void stop_when_ready(bool b);
int started() const { return m_started; }
void step_session_time(int seconds);

View File

@ -234,6 +234,13 @@ namespace libtorrent
//
struct TORRENT_EXPORT torrent_handle
{
// TODO: 3 consider replacing all the setters and getters for pause,
// resume, stop-when-ready, share-mode, upload-mode, super-seeding,
// apply-ip-filter, resolve-countries, pinned, sequential-download,
// seed-mode
// with just set_flags() and clear_flags() using the flags from
// add_torrent_params. Perhaps those flags should have a more generic
// name.
friend class invariant_access;
friend struct aux::session_impl;
friend class session;
@ -474,6 +481,9 @@ namespace libtorrent
void replace_trackers(std::vector<announce_entry> const&) const;
void add_tracker(announce_entry const&) const;
// TODO: 3 unify url_seed and http_seed with just web_seed, using the
// web_seed_entry.
// ``add_url_seed()`` adds another url to the torrent's list of url
// seeds. If the given url already exists in that list, the call has no
// effect. The torrent will connect to the server and try to download
@ -565,6 +575,24 @@ namespace libtorrent
//
// *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.
//
// Note that the torrent may transition into a downloading state while
// calling this function, and since the logic is edge triggered you may
// miss the edge. To avoid this race, if the torrent already is in a
// downloading state when this call is made, it will trigger the
// stop-when-ready immediately.
//
// When the stop-when-ready logic fires, the flag is cleared. Any
// subsequent transitions between downloading and non-downloading states
// will not be affected, until this function is used to set it again.
//
// The behavior is more robust when setting this flag as part of adding
// the torrent. See add_torrent_params.
//
// The stop-when-ready flag fixes the inherent race condition of waiting
// for the state_changed_alert and then call pause(). The download/seeding
// will most likely start in between posting the alert and receiving the
// call to pause.
void stop_when_ready(bool b) const;
// Explicitly sets the upload mode of the torrent. In upload mode, the

View File

@ -327,7 +327,8 @@ void setup_swarm(int num_nodes
// only print alerts from the session under test
lt::time_duration d = a->timestamp() - start_time;
boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count();
printf("%4d.%03d: %s\n", millis / 1000, millis % 1000
printf("%4d.%03d: %-25s %s\n", millis / 1000, millis % 1000
, a->what()
, a->message().c_str());
// if a torrent was added save the torrent handle

View File

@ -656,6 +656,195 @@ TORRENT_TEST(stop_when_ready)
}
});
}
// This test makes sure that the fastresume check will still run, and
// potentially fail, for stopped torrents. The actual checking of files won't
// start until the torrent is un-paused/resumed though
TORRENT_TEST(resume_reject_when_paused)
{
run_test(
[](settings_pack& sett) {
sett.set_int(settings_pack::alert_mask, alert::all_categories);
},
[](lt::session& ses) {
// add torrents
lt::add_torrent_params params = create_torrent(0, true);
// the torrent is not auto managed and paused. Once the resume data
// check completes, it will stay paused but the resume_rejected_alert
// will be posted
params.flags &= ~add_torrent_params::flag_auto_managed;
params.flags |= add_torrent_params::flag_paused;
// an empty dictionary will be rejected
params.resume_data = {'d', 'e'};
ses.async_add_torrent(params);
},
[](lt::session& ses) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
lt::time_point start_time = alerts[0]->timestamp();
int num_piece_finished = 0;
int resume_rejected = 0;
int state_changed = 0;
for (alert* a : alerts)
{
fprintf(stderr, "%-3d %-25s %s\n", int(duration_cast<lt::seconds>(a->timestamp()
- start_time).count())
, a->what()
, a->message().c_str());
if (alert_cast<piece_finished_alert>(a))
++num_piece_finished;
if (alert_cast<fastresume_rejected_alert>(a))
++resume_rejected;
if (auto sc = alert_cast<state_changed_alert>(a))
{
if (sc->state == torrent_status::checking_files)
++state_changed;
}
}
for (torrent_handle const& h : ses.get_torrents())
{
// the torrent should have been force-stopped. 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 checking files, because the resume data should have
// failed validation.
TEST_EQUAL(st.state, torrent_status::checking_files);
}
TEST_EQUAL(num_piece_finished, 0);
TEST_EQUAL(resume_rejected, 1);
TEST_EQUAL(state_changed, 1);
});
}
// this test adds the torrent in paused state and no resume data. Expecting the
// resume check to complete and just transition into checking state, but without
// actually checking anything
TORRENT_TEST(no_resume_when_paused)
{
run_test(
[](settings_pack& sett) {
sett.set_int(settings_pack::alert_mask, alert::all_categories);
},
[](lt::session& ses) {
// add torrents
lt::add_torrent_params params = create_torrent(0, true);
// the torrent is not auto managed and paused.
params.flags &= ~add_torrent_params::flag_auto_managed;
params.flags |= add_torrent_params::flag_paused;
ses.async_add_torrent(params);
},
[](lt::session& ses) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
lt::time_point start_time = alerts[0]->timestamp();
int num_piece_finished = 0;
int resume_rejected = 0;
int state_changed = 0;
for (alert* a : alerts)
{
fprintf(stderr, "%-3d %-25s %s\n", int(duration_cast<lt::seconds>(a->timestamp()
- start_time).count())
, a->what()
, a->message().c_str());
if (alert_cast<piece_finished_alert>(a))
++num_piece_finished;
if (alert_cast<fastresume_rejected_alert>(a))
++resume_rejected;
if (auto sc = alert_cast<state_changed_alert>(a))
{
if (sc->state == torrent_status::checking_files)
++state_changed;
}
}
for (torrent_handle const& h : ses.get_torrents())
{
// the torrent should have been force-stopped. 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 checking files, because the resume data should have
// failed validation.
TEST_EQUAL(st.state, torrent_status::checking_files);
}
TEST_EQUAL(num_piece_finished, 0);
TEST_EQUAL(resume_rejected, 0);
TEST_EQUAL(state_changed, 1);
});
}
// this is just asserting that when the files are checked we do in fact get
// piece_finished_alerts. The other tests rely on this assumption
TORRENT_TEST(no_resume_when_started)
{
run_test(
[](settings_pack& sett) {
sett.set_int(settings_pack::alert_mask, alert::all_categories);
},
[](lt::session& ses) {
// add torrents
lt::add_torrent_params params = create_torrent(0, true);
ses.async_add_torrent(params);
},
[](lt::session& ses) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
lt::time_point start_time = alerts[0]->timestamp();
int num_piece_finished = 0;
int state_changed = 0;
for (alert* a : alerts)
{
fprintf(stderr, "%-3d %-25s %s\n", int(duration_cast<lt::seconds>(a->timestamp()
- start_time).count())
, a->what()
, a->message().c_str());
if (alert_cast<piece_finished_alert>(a))
++num_piece_finished;
if (auto sc = alert_cast<state_changed_alert>(a))
{
if (sc->state == torrent_status::checking_files)
++state_changed;
}
}
TEST_EQUAL(num_piece_finished, 9);
TEST_EQUAL(state_changed, 1);
});
}
// 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

@ -11695,6 +11695,24 @@ namespace libtorrent
}
}
void torrent::stop_when_ready(bool b)
{
m_stop_when_ready = b;
// to avoid race condition, if we're already in a downloading state,
// trigger the stop-when-ready logic immediately.
if (m_stop_when_ready
&& is_downloading_state(m_state))
{
#ifndef TORRENT_DISABLE_LOGGING
debug_log("stop_when_ready triggered");
#endif
auto_managed(false);
pause();
m_stop_when_ready = false;
}
}
void torrent::set_state(torrent_status::state_t s)
{
TORRENT_ASSERT(is_single_thread());
@ -11741,6 +11759,7 @@ namespace libtorrent
// either upload or download (for the purpose of this flag).
auto_managed(false);
pause();
m_stop_when_ready = false;
}
m_state = s;