forked from premiere/premiere-libtorrent
fix multible torrent status timer bugs related to move time (#1492)
fix last_upload and last_download overflow after 9 hours in past. change last_upload, last_download, finished_time, resume, upload_mode_time to time_point and duration
This commit is contained in:
parent
3ef4109bf3
commit
a9d6d54111
|
@ -1,3 +1,4 @@
|
||||||
|
* fix last_upload and last_download overflow after 9 hours in past
|
||||||
* python binding add more add_torrent_params fields and an invalid key check
|
* python binding add more add_torrent_params fields and an invalid key check
|
||||||
* introduce introduce distinct types for peer_class_t, piece_index_t and
|
* introduce introduce distinct types for peer_class_t, piece_index_t and
|
||||||
file_index_t.
|
file_index_t.
|
||||||
|
|
|
@ -5,6 +5,7 @@ import libtorrent as lt
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
import time
|
import time
|
||||||
|
import datetime
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import binascii
|
import binascii
|
||||||
|
@ -80,6 +81,18 @@ class test_torrent_handle(unittest.TestCase):
|
||||||
self.setup()
|
self.setup()
|
||||||
self.h.clear_piece_deadlines()
|
self.h.clear_piece_deadlines()
|
||||||
|
|
||||||
|
def test_status_last_uploaded_dowloaded(self):
|
||||||
|
# we want to check at seconds precision but can't control session
|
||||||
|
# time, wait for next full second to prevent second increment
|
||||||
|
time.sleep(1 - datetime.datetime.now().microsecond / 1000000.0)
|
||||||
|
|
||||||
|
sessionStart = datetime.datetime.now().replace(microsecond=0)
|
||||||
|
self.setup()
|
||||||
|
st = self.h.status()
|
||||||
|
# last upload and download times are at session start time
|
||||||
|
self.assertEqual(st.last_upload, sessionStart)
|
||||||
|
self.assertEqual(st.last_download, sessionStart)
|
||||||
|
|
||||||
def test_torrent_status(self):
|
def test_torrent_status(self):
|
||||||
self.setup()
|
self.setup()
|
||||||
st = self.h.status()
|
st = self.h.status()
|
||||||
|
@ -268,6 +281,7 @@ class test_alerts(unittest.TestCase):
|
||||||
'enable_dht': False})
|
'enable_dht': False})
|
||||||
ses.async_add_torrent(
|
ses.async_add_torrent(
|
||||||
{"ti": lt.torrent_info("base.torrent"), "save_path": "."})
|
{"ti": lt.torrent_info("base.torrent"), "save_path": "."})
|
||||||
|
|
||||||
# this will cause an error (because of duplicate torrents) and the
|
# this will cause an error (because of duplicate torrents) and the
|
||||||
# torrent_info object created here will be deleted once the alert goes out
|
# torrent_info object created here will be deleted once the alert goes out
|
||||||
# of scope. When that happens, it will decrement the python object, to allow
|
# of scope. When that happens, it will decrement the python object, to allow
|
||||||
|
|
|
@ -95,6 +95,8 @@ namespace libtorrent
|
||||||
class bt_peer_connection;
|
class bt_peer_connection;
|
||||||
struct listen_socket_t;
|
struct listen_socket_t;
|
||||||
|
|
||||||
|
using seconds32 = std::chrono::duration<std::int32_t>;
|
||||||
|
|
||||||
enum class waste_reason
|
enum class waste_reason
|
||||||
{
|
{
|
||||||
piece_timed_out, piece_cancelled, piece_unknown, piece_seed
|
piece_timed_out, piece_cancelled, piece_unknown, piece_seed
|
||||||
|
@ -478,14 +480,15 @@ namespace libtorrent
|
||||||
|
|
||||||
void stop_when_ready(bool b);
|
void stop_when_ready(bool b);
|
||||||
|
|
||||||
int started() const { return m_started; }
|
time_point started() const { return m_started; }
|
||||||
void step_session_time(int seconds);
|
void step_session_time(int seconds);
|
||||||
void do_pause(bool clear_disk_cache = true);
|
void do_pause(bool clear_disk_cache = true);
|
||||||
void do_resume();
|
void do_resume();
|
||||||
|
|
||||||
int finished_time() const;
|
seconds32 finished_time() const;
|
||||||
int active_time() const;
|
seconds32 active_time() const;
|
||||||
int seeding_time() const;
|
seconds32 seeding_time() const;
|
||||||
|
seconds32 upload_mode_time() const;
|
||||||
|
|
||||||
bool is_paused() const;
|
bool is_paused() const;
|
||||||
bool is_torrent_paused() const { return m_paused; }
|
bool is_torrent_paused() const { return m_paused; }
|
||||||
|
@ -496,7 +499,8 @@ namespace libtorrent
|
||||||
{
|
{
|
||||||
// save resume data every 15 minutes regardless, just to
|
// save resume data every 15 minutes regardless, just to
|
||||||
// keep stats up to date
|
// keep stats up to date
|
||||||
return m_need_save_resume_data || m_ses.session_time() - m_last_saved_resume > 15 * 60;
|
return m_need_save_resume_data ||
|
||||||
|
aux::time_now() - m_last_saved_resume > minutes(15);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_need_save_resume()
|
void set_need_save_resume()
|
||||||
|
@ -1034,7 +1038,7 @@ namespace libtorrent
|
||||||
// that are not private
|
// that are not private
|
||||||
void lsd_announce();
|
void lsd_announce();
|
||||||
|
|
||||||
void update_last_upload() { m_last_upload = int16_t(m_ses.session_time()); }
|
void update_last_upload() { m_last_upload = aux::time_now(); }
|
||||||
|
|
||||||
void set_apply_ip_filter(bool b);
|
void set_apply_ip_filter(bool b);
|
||||||
bool apply_ip_filter() const { return m_apply_ip_filter; }
|
bool apply_ip_filter() const { return m_apply_ip_filter; }
|
||||||
|
@ -1314,9 +1318,7 @@ namespace libtorrent
|
||||||
// m_num_verified = m_verified.count()
|
// m_num_verified = m_verified.count()
|
||||||
std::uint32_t m_num_verified = 0;
|
std::uint32_t m_num_verified = 0;
|
||||||
|
|
||||||
// this timestamp is kept in session-time, to
|
time_point m_last_saved_resume = aux::time_now();
|
||||||
// make it fit in 16 bits
|
|
||||||
std::uint16_t m_last_saved_resume;
|
|
||||||
|
|
||||||
// if this torrent is running, this was the time
|
// if this torrent is running, this was the time
|
||||||
// when it was started. This is used to have a
|
// when it was started. This is used to have a
|
||||||
|
@ -1326,15 +1328,15 @@ namespace libtorrent
|
||||||
// in session-time. see session_impl for details.
|
// in session-time. see session_impl for details.
|
||||||
// the reference point is stepped forward every 4
|
// the reference point is stepped forward every 4
|
||||||
// hours to keep the timestamps fit in 16 bits
|
// hours to keep the timestamps fit in 16 bits
|
||||||
std::uint16_t m_started;
|
time_point m_started = aux::time_now();
|
||||||
|
|
||||||
// if we're a seed, this is the session time
|
// if we're a seed, this is the session time
|
||||||
// timestamp of when we became one
|
// timestamp of when we became one
|
||||||
std::uint16_t m_became_seed = 0;
|
time_point m_became_seed = aux::time_now();
|
||||||
|
|
||||||
// if we're finished, this is the session time
|
// if we're finished, this is the session time
|
||||||
// timestamp of when we finished
|
// timestamp of when we finished
|
||||||
std::uint16_t m_became_finished = 0;
|
time_point m_became_finished = aux::time_now();
|
||||||
|
|
||||||
// when checking, this is the first piece we have not
|
// when checking, this is the first piece we have not
|
||||||
// issued a hash job for
|
// issued a hash job for
|
||||||
|
@ -1383,7 +1385,7 @@ namespace libtorrent
|
||||||
|
|
||||||
// the session time timestamp of when we entered upload mode
|
// the session time timestamp of when we entered upload mode
|
||||||
// if we're currently in upload-mode
|
// if we're currently in upload-mode
|
||||||
std::uint16_t m_upload_mode_time = 0;
|
time_point m_upload_mode_time = aux::time_now();
|
||||||
|
|
||||||
// true when this torrent should announce to
|
// true when this torrent should announce to
|
||||||
// trackers
|
// trackers
|
||||||
|
@ -1425,7 +1427,7 @@ namespace libtorrent
|
||||||
// paused. specified in seconds. This only track time _before_ we started
|
// paused. specified in seconds. This only track time _before_ we started
|
||||||
// the torrent this last time. When the torrent is paused, this counter is
|
// the torrent this last time. When the torrent is paused, this counter is
|
||||||
// incremented to include this current session.
|
// incremented to include this current session.
|
||||||
unsigned int m_active_time:24;
|
seconds32 m_active_time;
|
||||||
|
|
||||||
// the index to the last tracker that worked
|
// the index to the last tracker that worked
|
||||||
std::int8_t m_last_working_tracker = -1;
|
std::int8_t m_last_working_tracker = -1;
|
||||||
|
@ -1434,7 +1436,7 @@ namespace libtorrent
|
||||||
|
|
||||||
// total time we've been finished with this torrent.
|
// total time we've been finished with this torrent.
|
||||||
// does not count when the torrent is stopped or paused.
|
// does not count when the torrent is stopped or paused.
|
||||||
unsigned int m_finished_time:24;
|
seconds32 m_finished_time;
|
||||||
|
|
||||||
// in case the piece picker hasn't been constructed
|
// in case the piece picker hasn't been constructed
|
||||||
// when this settings is set, this variable will keep
|
// when this settings is set, this variable will keep
|
||||||
|
@ -1474,7 +1476,7 @@ namespace libtorrent
|
||||||
// accounts for the time prior to the current start of the torrent. When
|
// accounts for the time prior to the current start of the torrent. When
|
||||||
// the torrent is paused, this counter is incremented to account for the
|
// the torrent is paused, this counter is incremented to account for the
|
||||||
// additional seeding time.
|
// additional seeding time.
|
||||||
unsigned int m_seeding_time:24;
|
seconds32 m_seeding_time;
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
|
||||||
|
@ -1562,7 +1564,7 @@ namespace libtorrent
|
||||||
// the timestamp of the last piece passed for this torrent specified in
|
// the timestamp of the last piece passed for this torrent specified in
|
||||||
// session_time. This is signed because it must be able to represent time
|
// session_time. This is signed because it must be able to represent time
|
||||||
// before the session started
|
// before the session started
|
||||||
std::int16_t m_last_download = (std::numeric_limits<std::int16_t>::min)();
|
time_point m_last_download = aux::time_now();
|
||||||
|
|
||||||
// the number of peer connections to seeds. This should be the same as
|
// the number of peer connections to seeds. This should be the same as
|
||||||
// counting the peer connections that say true for is_seed()
|
// counting the peer connections that say true for is_seed()
|
||||||
|
@ -1575,7 +1577,7 @@ namespace libtorrent
|
||||||
// the timestamp of the last byte uploaded from this torrent specified in
|
// the timestamp of the last byte uploaded from this torrent specified in
|
||||||
// session_time. This is signed because it must be able to represent time
|
// session_time. This is signed because it must be able to represent time
|
||||||
// before the session started.
|
// before the session started.
|
||||||
std::int16_t m_last_upload = (std::numeric_limits<std::int16_t>::min)();
|
time_point m_last_upload = aux::time_now();
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
|
||||||
|
|
|
@ -546,7 +546,9 @@ namespace libtorrent
|
||||||
// the info-hash for this torrent
|
// the info-hash for this torrent
|
||||||
sha1_hash info_hash;
|
sha1_hash info_hash;
|
||||||
|
|
||||||
|
// This value is not persistent and get set to session start time.
|
||||||
time_point last_upload;
|
time_point last_upload;
|
||||||
|
// This value is not persistent and get set to session start time.
|
||||||
time_point last_download;
|
time_point last_download;
|
||||||
|
|
||||||
seconds active_duration;
|
seconds active_duration;
|
||||||
|
|
|
@ -79,24 +79,294 @@ TORRENT_TEST(status_timers)
|
||||||
if ((ticks % 3600) == 0)
|
if ((ticks % 3600) == 0)
|
||||||
{
|
{
|
||||||
lt::time_point const now = lt::clock_type::now();
|
lt::time_point const now = lt::clock_type::now();
|
||||||
auto const since_start = duration_cast<seconds>(now - start_time) - lt::seconds(1);
|
// finish is 1 tick after start
|
||||||
|
auto const since_finish = duration_cast<seconds>(now - start_time) - lt::seconds(1);
|
||||||
torrent_status st = handle.status();
|
torrent_status st = handle.status();
|
||||||
TEST_CHECK(st.active_duration == since_start);
|
TEST_EQUAL(st.active_duration.count(), since_finish.count());
|
||||||
TEST_CHECK(st.seeding_duration == since_start);
|
TEST_EQUAL(st.seeding_duration.count(), since_finish.count());
|
||||||
TEST_CHECK(st.finished_duration == since_start);
|
TEST_EQUAL(st.finished_duration.count(), since_finish.count());
|
||||||
TEST_CHECK(st.last_upload < start_time);
|
|
||||||
|
|
||||||
// checking the torrent counts as downloading
|
// does not upload without peers
|
||||||
// eventually though, we've forgotten about it and go back to -1
|
TEST_CHECK(st.last_upload == start_time);
|
||||||
if (since_start > lt::seconds(65000))
|
// does not download in seeding mode
|
||||||
{
|
TEST_CHECK(st.last_download == start_time);
|
||||||
TEST_CHECK(st.last_download < start_time);
|
}
|
||||||
}
|
return false;
|
||||||
else
|
});
|
||||||
{
|
TEST_CHECK(ran_to_completion);
|
||||||
// TODO: this should really be a proximity-check
|
}
|
||||||
TEST_CHECK(st.last_download == start_time + lt::seconds(1));
|
|
||||||
}
|
TORRENT_TEST(status_timers_last_upload)
|
||||||
|
{
|
||||||
|
bool ran_to_completion = false;
|
||||||
|
|
||||||
|
lt::time_point start_time;
|
||||||
|
lt::torrent_handle handle;
|
||||||
|
|
||||||
|
setup_swarm(2, swarm_test::upload
|
||||||
|
// add session
|
||||||
|
, [](lt::settings_pack&) {}
|
||||||
|
// add torrent
|
||||||
|
, [](lt::add_torrent_params&) {}
|
||||||
|
// on alert
|
||||||
|
, [&](lt::alert const* a, lt::session&) {
|
||||||
|
if (auto ta = alert_cast<torrent_added_alert>(a))
|
||||||
|
{
|
||||||
|
TEST_CHECK(!handle.is_valid());
|
||||||
|
start_time = lt::clock_type::now();
|
||||||
|
handle = ta->handle;
|
||||||
|
torrent_status st = handle.status();
|
||||||
|
// test last upload and download state before wo go throgh
|
||||||
|
// torrent states
|
||||||
|
TEST_CHECK(st.last_download == start_time);
|
||||||
|
TEST_CHECK(st.last_upload == start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// terminate
|
||||||
|
, [&](int ticks, lt::session&) -> bool
|
||||||
|
{
|
||||||
|
if (ticks > 10)
|
||||||
|
{
|
||||||
|
ran_to_completion = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent_status st = handle.status();
|
||||||
|
// uploadtime is 0 seconds behind now
|
||||||
|
TEST_EQUAL(duration_cast<seconds>(st.last_upload - lt::clock_type::now()).count(), 0);
|
||||||
|
// does not download in seeding mode
|
||||||
|
TEST_CHECK(st.last_download == start_time);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
TEST_CHECK(ran_to_completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(status_timers_time_shift_with_active_torrent)
|
||||||
|
{
|
||||||
|
bool ran_to_completion = false;
|
||||||
|
|
||||||
|
lt::time_point start_time;
|
||||||
|
lt::torrent_handle handle;
|
||||||
|
seconds expected_active_duration = seconds(0);
|
||||||
|
bool tick_is_in_active_range = false;
|
||||||
|
int tick_check_intervall = 1;
|
||||||
|
|
||||||
|
setup_swarm(1, swarm_test::upload
|
||||||
|
// add session
|
||||||
|
, [](lt::settings_pack&) {}
|
||||||
|
// add torrent
|
||||||
|
, [](lt::add_torrent_params&) {}
|
||||||
|
// on alert
|
||||||
|
, [&](lt::alert const* a, lt::session&) {
|
||||||
|
if (auto ta = alert_cast<torrent_added_alert>(a))
|
||||||
|
{
|
||||||
|
TEST_CHECK(!handle.is_valid());
|
||||||
|
start_time = lt::clock_type::now();
|
||||||
|
handle = ta->handle;
|
||||||
|
torrent_status st = handle.status();
|
||||||
|
// test last upload and download state before wo go throgh
|
||||||
|
// torrent states
|
||||||
|
TEST_CHECK(st.last_download == start_time);
|
||||||
|
TEST_CHECK(st.last_upload == start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// terminate
|
||||||
|
, [&](int ticks, lt::session&) -> bool
|
||||||
|
{
|
||||||
|
if(tick_is_in_active_range){
|
||||||
|
// 1 second per tick
|
||||||
|
expected_active_duration++;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(ticks)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// torrent get ready for seeding on first tick, means time +1s
|
||||||
|
tick_is_in_active_range = true;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// pause after we did have the first upload tick
|
||||||
|
handle.pause();
|
||||||
|
tick_is_in_active_range = false;
|
||||||
|
break;
|
||||||
|
case 64000:
|
||||||
|
// resume just before we hit the time shift handling
|
||||||
|
// this is needed to test what happend if we want to
|
||||||
|
// shift more time then we have active time because
|
||||||
|
// we shift 4 hours and have less then 1 hours active time
|
||||||
|
handle.resume();
|
||||||
|
tick_is_in_active_range = true;
|
||||||
|
// don't check every tick
|
||||||
|
tick_check_intervall = 600;
|
||||||
|
break;
|
||||||
|
case 68000:
|
||||||
|
// simulate at least 68000 seconds because timestamps are
|
||||||
|
// 16 bits counting seconds
|
||||||
|
ran_to_completion = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that the timers seem correct
|
||||||
|
if (tick_is_in_active_range && (ticks % tick_check_intervall) == 0)
|
||||||
|
{
|
||||||
|
torrent_status st = handle.status();
|
||||||
|
TEST_EQUAL(st.active_duration.count(), expected_active_duration.count());
|
||||||
|
TEST_EQUAL(st.seeding_duration.count(), expected_active_duration.count());
|
||||||
|
TEST_EQUAL(st.finished_duration.count(), expected_active_duration.count());
|
||||||
|
// does not upload without peers
|
||||||
|
TEST_CHECK(st.last_upload == start_time);
|
||||||
|
// does not download in seeding mode
|
||||||
|
TEST_CHECK(st.last_download == start_time);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
TEST_CHECK(ran_to_completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(finish_time_shift_active)
|
||||||
|
{
|
||||||
|
bool ran_to_completion = false;
|
||||||
|
|
||||||
|
lt::time_point start_time;
|
||||||
|
lt::torrent_handle handle;
|
||||||
|
seconds expected_active_duration = seconds(0);
|
||||||
|
bool tick_is_in_active_range = false;
|
||||||
|
|
||||||
|
setup_swarm(1, swarm_test::upload
|
||||||
|
// add session
|
||||||
|
, [](lt::settings_pack&) {}
|
||||||
|
// add torrent
|
||||||
|
, [](lt::add_torrent_params&) {}
|
||||||
|
// on alert
|
||||||
|
, [&](lt::alert const* a, lt::session&) {
|
||||||
|
if (auto ta = alert_cast<torrent_added_alert>(a))
|
||||||
|
{
|
||||||
|
TEST_CHECK(!handle.is_valid());
|
||||||
|
start_time = lt::clock_type::now();
|
||||||
|
handle = ta->handle;
|
||||||
|
torrent_status st = handle.status();
|
||||||
|
// test last upload and download state before wo go throgh
|
||||||
|
// torrent states
|
||||||
|
TEST_CHECK(st.last_download == start_time);
|
||||||
|
TEST_CHECK(st.last_upload == start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// terminate
|
||||||
|
, [&](int ticks, lt::session&) -> bool
|
||||||
|
{
|
||||||
|
if(tick_is_in_active_range){
|
||||||
|
// 1 second per tick
|
||||||
|
expected_active_duration++;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(ticks)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// torrent get ready for seeding on first tick, means time +1s
|
||||||
|
tick_is_in_active_range = true;
|
||||||
|
break;
|
||||||
|
case 7000:
|
||||||
|
// pause before 4 hours to get a become finish timestamp which
|
||||||
|
// will be clamped
|
||||||
|
handle.pause();
|
||||||
|
// resume to get an become finish update
|
||||||
|
handle.resume();
|
||||||
|
tick_is_in_active_range = true;
|
||||||
|
break;
|
||||||
|
case 70000:
|
||||||
|
// simulate at least 70000 seconds because timestamps are
|
||||||
|
// 16 bits counting seconds
|
||||||
|
ran_to_completion = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that the timers seem correct
|
||||||
|
if ((ticks % 3600) == 0)
|
||||||
|
{
|
||||||
|
torrent_status st = handle.status();
|
||||||
|
TEST_EQUAL(st.active_duration.count(), expected_active_duration.count());
|
||||||
|
TEST_EQUAL(st.seeding_duration.count(), expected_active_duration.count());
|
||||||
|
TEST_EQUAL(st.finished_duration.count(), expected_active_duration.count());
|
||||||
|
// does not upload without peers
|
||||||
|
TEST_CHECK(st.last_upload == start_time);
|
||||||
|
// does not download in seeding mode
|
||||||
|
TEST_CHECK(st.last_download == start_time);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
TEST_CHECK(ran_to_completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(finish_time_shift_paused)
|
||||||
|
{
|
||||||
|
bool ran_to_completion = false;
|
||||||
|
|
||||||
|
lt::time_point start_time;
|
||||||
|
lt::torrent_handle handle;
|
||||||
|
seconds expected_active_duration = seconds(0);
|
||||||
|
bool tick_is_in_active_range = false;
|
||||||
|
|
||||||
|
setup_swarm(1, swarm_test::upload
|
||||||
|
// add session
|
||||||
|
, [](lt::settings_pack&) {}
|
||||||
|
// add torrent
|
||||||
|
, [](lt::add_torrent_params&) {}
|
||||||
|
// on alert
|
||||||
|
, [&](lt::alert const* a, lt::session&) {
|
||||||
|
if (auto ta = alert_cast<torrent_added_alert>(a))
|
||||||
|
{
|
||||||
|
TEST_CHECK(!handle.is_valid());
|
||||||
|
start_time = lt::clock_type::now();
|
||||||
|
handle = ta->handle;
|
||||||
|
torrent_status st = handle.status();
|
||||||
|
// test last upload and download state before wo go throgh
|
||||||
|
// torrent states
|
||||||
|
TEST_CHECK(st.last_download == start_time);
|
||||||
|
TEST_CHECK(st.last_upload == start_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// terminate
|
||||||
|
, [&](int ticks, lt::session&) -> bool
|
||||||
|
{
|
||||||
|
if(tick_is_in_active_range){
|
||||||
|
// 1 second per tick
|
||||||
|
expected_active_duration++;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(ticks)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
// torrent get ready for seeding on first tick, means time +1s
|
||||||
|
tick_is_in_active_range = true;
|
||||||
|
break;
|
||||||
|
case 7000:
|
||||||
|
// pause before 4 hours to get a become finish timestamp which
|
||||||
|
// will be clamped
|
||||||
|
handle.pause();
|
||||||
|
// resume to get an become finish update
|
||||||
|
handle.resume();
|
||||||
|
// pause to test timeshift in paused state
|
||||||
|
handle.pause();
|
||||||
|
tick_is_in_active_range = false;
|
||||||
|
break;
|
||||||
|
case 70000:
|
||||||
|
// simulate at least 70000 seconds because timestamps are
|
||||||
|
// 16 bits counting seconds
|
||||||
|
ran_to_completion = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that the timers seem correct
|
||||||
|
if (tick_is_in_active_range && (ticks % 3600) == 0)
|
||||||
|
{
|
||||||
|
torrent_status st = handle.status();
|
||||||
|
TEST_EQUAL(st.active_duration.count(), expected_active_duration.count());
|
||||||
|
TEST_EQUAL(st.seeding_duration.count(), expected_active_duration.count());
|
||||||
|
TEST_EQUAL(st.finished_duration.count(), expected_active_duration.count());
|
||||||
|
// does not upload without peers
|
||||||
|
TEST_CHECK(st.last_upload == start_time);
|
||||||
|
// does not download in seeding mode
|
||||||
|
TEST_CHECK(st.last_download == start_time);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
164
src/torrent.cpp
164
src/torrent.cpp
|
@ -102,6 +102,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
using std::chrono::duration_cast;
|
||||||
|
using std::chrono::seconds;
|
||||||
|
using libtorrent::seconds32;
|
||||||
|
|
||||||
namespace libtorrent
|
namespace libtorrent
|
||||||
{
|
{
|
||||||
|
@ -121,7 +124,6 @@ namespace libtorrent
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int default_piece_priority = 4;
|
constexpr int default_piece_priority = 4;
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
@ -180,8 +182,8 @@ namespace libtorrent
|
||||||
, m_storage_constructor(p.storage)
|
, m_storage_constructor(p.storage)
|
||||||
, m_added_time(time(nullptr))
|
, m_added_time(time(nullptr))
|
||||||
, m_info_hash(info_hash)
|
, m_info_hash(info_hash)
|
||||||
, m_last_saved_resume(ses.session_time())
|
, m_last_saved_resume(aux::time_now())
|
||||||
, m_started(ses.session_time())
|
, m_started(aux::time_now())
|
||||||
, m_error_file(torrent_status::error_file_none)
|
, m_error_file(torrent_status::error_file_none)
|
||||||
, m_sequence_number(seq)
|
, m_sequence_number(seq)
|
||||||
, m_announce_to_trackers((p.flags & add_torrent_params::flag_paused) == 0)
|
, m_announce_to_trackers((p.flags & add_torrent_params::flag_paused) == 0)
|
||||||
|
@ -373,9 +375,9 @@ namespace libtorrent
|
||||||
|
|
||||||
// the number of seconds this torrent has spent in started, finished and
|
// the number of seconds this torrent has spent in started, finished and
|
||||||
// seeding state so far, respectively.
|
// seeding state so far, respectively.
|
||||||
m_active_time = p.active_time;
|
m_active_time = seconds(p.active_time);
|
||||||
m_finished_time = p.finished_time;
|
m_finished_time = seconds(p.finished_time);
|
||||||
m_seeding_time = p.seeding_time;
|
m_seeding_time = seconds(p.seeding_time);
|
||||||
|
|
||||||
m_added_time = p.added_time ? p.added_time : time(nullptr);
|
m_added_time = p.added_time ? p.added_time : time(nullptr);
|
||||||
m_completed_time = p.completed_time;
|
m_completed_time = p.completed_time;
|
||||||
|
@ -961,7 +963,7 @@ namespace libtorrent
|
||||||
p->cancel_all_requests();
|
p->cancel_all_requests();
|
||||||
}
|
}
|
||||||
// this is used to try leaving upload only mode periodically
|
// this is used to try leaving upload only mode periodically
|
||||||
m_upload_mode_time = m_ses.session_time();
|
m_upload_mode_time = aux::time_now();
|
||||||
}
|
}
|
||||||
else if (m_peer_list)
|
else if (m_peer_list)
|
||||||
{
|
{
|
||||||
|
@ -3849,7 +3851,7 @@ namespace libtorrent
|
||||||
// is deallocated by the torrent once it starts seeding
|
// is deallocated by the torrent once it starts seeding
|
||||||
}
|
}
|
||||||
|
|
||||||
m_last_download = m_ses.session_time();
|
m_last_download = aux::time_now();
|
||||||
|
|
||||||
if (m_share_mode)
|
if (m_share_mode)
|
||||||
recalc_share_mode();
|
recalc_share_mode();
|
||||||
|
@ -5950,9 +5952,10 @@ namespace libtorrent
|
||||||
ret["total_uploaded"] = m_total_uploaded;
|
ret["total_uploaded"] = m_total_uploaded;
|
||||||
ret["total_downloaded"] = m_total_downloaded;
|
ret["total_downloaded"] = m_total_downloaded;
|
||||||
|
|
||||||
ret["active_time"] = active_time();
|
// cast to seconds in case that internal values doesn't have ratio<1>
|
||||||
ret["finished_time"] = finished_time();
|
ret["active_time"] = duration_cast<seconds>(active_time()).count();
|
||||||
ret["seeding_time"] = seeding_time();
|
ret["finished_time"] = duration_cast<seconds>(finished_time()).count();
|
||||||
|
ret["seeding_time"] = duration_cast<seconds>(seeding_time()).count();
|
||||||
ret["last_seen_complete"] = m_last_seen_complete;
|
ret["last_seen_complete"] = m_last_seen_complete;
|
||||||
|
|
||||||
ret["num_complete"] = m_complete;
|
ret["num_complete"] = m_complete;
|
||||||
|
@ -7241,7 +7244,7 @@ namespace libtorrent
|
||||||
set_state(torrent_status::finished);
|
set_state(torrent_status::finished);
|
||||||
set_queue_position(-1);
|
set_queue_position(-1);
|
||||||
|
|
||||||
m_became_finished = m_ses.session_time();
|
m_became_finished = aux::time_now();
|
||||||
|
|
||||||
// we have to call completed() before we start
|
// we have to call completed() before we start
|
||||||
// disconnecting peers, since there's an assert
|
// disconnecting peers, since there's an assert
|
||||||
|
@ -7358,7 +7361,7 @@ namespace libtorrent
|
||||||
maybe_done_flushing();
|
maybe_done_flushing();
|
||||||
|
|
||||||
set_state(torrent_status::seeding);
|
set_state(torrent_status::seeding);
|
||||||
m_became_seed = m_ses.session_time();
|
m_became_seed = aux::time_now();
|
||||||
|
|
||||||
if (!m_announcing) return;
|
if (!m_announcing) return;
|
||||||
|
|
||||||
|
@ -8220,14 +8223,14 @@ namespace libtorrent
|
||||||
if (a < b) return 0;
|
if (a < b) return 0;
|
||||||
return std::uint16_t(a - b);
|
return std::uint16_t(a - b);
|
||||||
}
|
}
|
||||||
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
std::int16_t clamped_subtract_s16(int a, int b)
|
std::int16_t clamped_subtract_s16(int a, int b)
|
||||||
{
|
{
|
||||||
if (a + (std::numeric_limits<std::int16_t>::min)() < b)
|
if (a + (std::numeric_limits<std::int16_t>::min)() < b)
|
||||||
return (std::numeric_limits<std::int16_t>::min)();
|
return (std::numeric_limits<std::int16_t>::min)();
|
||||||
return std::int16_t(a - b);
|
return std::int16_t(a - b);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
// this is called every time the session timer takes a step back. Since the
|
// this is called every time the session timer takes a step back. Since the
|
||||||
|
@ -8249,44 +8252,9 @@ namespace libtorrent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// m_active_time, m_seeding_time and m_finished_time are absolute counters
|
|
||||||
// of the historical time we've spent in each state. The current time
|
|
||||||
// we've spent in those states (this session) is calculated by
|
|
||||||
// session_time() - m_started
|
|
||||||
// session_time() - m_became_seed
|
|
||||||
// session_time() - m_became_finished respectively. If any of the
|
|
||||||
// comparison points were pulled back to the oldest representable value (0)
|
|
||||||
// the left-over time must be transferred into the m_*_time counters.
|
|
||||||
|
|
||||||
if (m_started < seconds && !is_paused())
|
|
||||||
{
|
|
||||||
int const lost_seconds = seconds - m_started;
|
|
||||||
m_active_time += lost_seconds;
|
|
||||||
}
|
|
||||||
m_started = clamped_subtract_u16(m_started, seconds);
|
|
||||||
|
|
||||||
if (m_became_seed < seconds && is_seed())
|
|
||||||
{
|
|
||||||
int const lost_seconds = seconds - m_became_seed;
|
|
||||||
m_seeding_time += lost_seconds;
|
|
||||||
}
|
|
||||||
m_became_seed = clamped_subtract_u16(m_became_seed, seconds);
|
|
||||||
|
|
||||||
if (m_became_finished < seconds && is_finished())
|
|
||||||
{
|
|
||||||
int const lost_seconds = seconds - m_became_finished;
|
|
||||||
m_finished_time += lost_seconds;
|
|
||||||
}
|
|
||||||
m_became_finished = clamped_subtract_u16(m_became_finished, seconds);
|
|
||||||
|
|
||||||
m_last_upload = clamped_subtract_s16(m_last_upload, seconds);
|
|
||||||
m_last_download = clamped_subtract_s16(m_last_download, seconds);
|
|
||||||
#ifndef TORRENT_NO_DEPRECATE
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
m_last_scrape = clamped_subtract_s16(m_last_scrape, seconds);
|
m_last_scrape = clamped_subtract_s16(m_last_scrape, seconds);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_last_saved_resume = clamped_subtract_u16(m_last_saved_resume, seconds);
|
|
||||||
m_upload_mode_time = clamped_subtract_u16(m_upload_mode_time, seconds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the higher seed rank, the more important to seed
|
// the higher seed rank, the more important to seed
|
||||||
|
@ -8308,15 +8276,16 @@ namespace libtorrent
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
std::int64_t const fin_time = finished_time();
|
seconds32 const act_time = active_time();
|
||||||
std::int64_t const download_time = int(active_time()) - fin_time;
|
seconds32 const fin_time = finished_time();
|
||||||
|
seconds32 const download_time = act_time - fin_time;
|
||||||
|
|
||||||
// if we haven't yet met the seed limits, set the seed_ratio_not_met
|
// if we haven't yet met the seed limits, set the seed_ratio_not_met
|
||||||
// flag. That will make this seed prioritized
|
// flag. That will make this seed prioritized
|
||||||
// downloaded may be 0 if the torrent is 0-sized
|
// downloaded may be 0 if the torrent is 0-sized
|
||||||
std::int64_t const downloaded = (std::max)(m_total_downloaded, m_torrent_file->total_size());
|
std::int64_t const downloaded = (std::max)(m_total_downloaded, m_torrent_file->total_size());
|
||||||
if (fin_time < s.get_int(settings_pack::seed_time_limit)
|
if (fin_time < seconds(s.get_int(settings_pack::seed_time_limit))
|
||||||
&& (download_time > 1
|
&& (download_time.count() > 1
|
||||||
&& fin_time * 100 / download_time < s.get_int(settings_pack::seed_time_ratio_limit))
|
&& fin_time * 100 / download_time < s.get_int(settings_pack::seed_time_ratio_limit))
|
||||||
&& downloaded > 0
|
&& downloaded > 0
|
||||||
&& m_total_uploaded * 100 / downloaded < s.get_int(settings_pack::share_ratio_limit))
|
&& m_total_uploaded * 100 / downloaded < s.get_int(settings_pack::share_ratio_limit))
|
||||||
|
@ -8324,7 +8293,7 @@ namespace libtorrent
|
||||||
|
|
||||||
// if this torrent is running, and it was started less
|
// if this torrent is running, and it was started less
|
||||||
// than 30 minutes ago, give it priority, to avoid oscillation
|
// than 30 minutes ago, give it priority, to avoid oscillation
|
||||||
if (!is_paused() && (m_ses.session_time() - m_started) < 30 * 60)
|
if (!is_paused() && act_time < minutes(30))
|
||||||
ret |= recently_started;
|
ret |= recently_started;
|
||||||
|
|
||||||
// if we have any scrape data, use it to calculate
|
// if we have any scrape data, use it to calculate
|
||||||
|
@ -8375,7 +8344,7 @@ namespace libtorrent
|
||||||
}
|
}
|
||||||
|
|
||||||
m_need_save_resume_data = false;
|
m_need_save_resume_data = false;
|
||||||
m_last_saved_resume = m_ses.session_time();
|
m_last_saved_resume = aux::time_now();
|
||||||
m_save_resume_flags = std::uint8_t(flags);
|
m_save_resume_flags = std::uint8_t(flags);
|
||||||
state_updated();
|
state_updated();
|
||||||
|
|
||||||
|
@ -8472,13 +8441,16 @@ namespace libtorrent
|
||||||
update_state_list();
|
update_state_list();
|
||||||
update_want_tick();
|
update_want_tick();
|
||||||
|
|
||||||
m_active_time += m_ses.session_time() - m_started;
|
const time_point now = aux::time_now();
|
||||||
|
|
||||||
if (is_seed())
|
m_active_time +=
|
||||||
m_seeding_time += m_ses.session_time() - m_became_seed;
|
duration_cast<seconds32>(now - m_started);
|
||||||
|
|
||||||
if (is_finished())
|
if (is_seed()) m_seeding_time +=
|
||||||
m_finished_time += m_ses.session_time() - m_became_finished;
|
duration_cast<seconds32>(now - m_became_seed);
|
||||||
|
|
||||||
|
if (is_finished()) m_finished_time +=
|
||||||
|
duration_cast<seconds32>(now - m_became_finished);
|
||||||
|
|
||||||
m_announce_to_dht = false;
|
m_announce_to_dht = false;
|
||||||
m_announce_to_trackers = false;
|
m_announce_to_trackers = false;
|
||||||
|
@ -8704,7 +8676,7 @@ namespace libtorrent
|
||||||
if (alerts().should_post<torrent_resumed_alert>())
|
if (alerts().should_post<torrent_resumed_alert>())
|
||||||
alerts().emplace_alert<torrent_resumed_alert>(get_handle());
|
alerts().emplace_alert<torrent_resumed_alert>(get_handle());
|
||||||
|
|
||||||
m_started = m_ses.session_time();
|
m_started = aux::time_now();
|
||||||
if (is_seed()) m_became_seed = m_started;
|
if (is_seed()) m_became_seed = m_started;
|
||||||
if (is_finished()) m_became_finished = m_started;
|
if (is_finished()) m_became_finished = m_started;
|
||||||
|
|
||||||
|
@ -8893,31 +8865,45 @@ namespace libtorrent
|
||||||
announce_with_tracker(tracker_request::stopped);
|
announce_with_tracker(tracker_request::stopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
int torrent::finished_time() const
|
seconds32 torrent::finished_time() const
|
||||||
{
|
{
|
||||||
// m_finished_time does not account for the current "session", just the
|
if(!is_finished() || is_paused())
|
||||||
// time before we last started this torrent. To get the current time, we
|
return m_finished_time;
|
||||||
// need to add the time since we started it
|
|
||||||
return m_finished_time + ((!is_finished() || is_paused()) ? 0
|
return m_finished_time + duration_cast<seconds32>(
|
||||||
: (m_ses.session_time() - m_became_finished));
|
aux::time_now() - m_became_finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
int torrent::active_time() const
|
seconds32 torrent::active_time() const
|
||||||
{
|
{
|
||||||
|
if(is_paused())
|
||||||
|
return m_active_time;
|
||||||
|
|
||||||
// m_active_time does not account for the current "session", just the
|
// m_active_time does not account for the current "session", just the
|
||||||
// time before we last started this torrent. To get the current time, we
|
// time before we last started this torrent. To get the current time, we
|
||||||
// need to add the time since we started it
|
// need to add the time since we started it
|
||||||
return m_active_time + (is_paused() ? 0
|
return m_active_time + duration_cast<seconds32>(
|
||||||
: m_ses.session_time() - m_started);
|
aux::time_now() - m_started);
|
||||||
}
|
}
|
||||||
|
|
||||||
int torrent::seeding_time() const
|
seconds32 torrent::seeding_time() const
|
||||||
{
|
{
|
||||||
|
if(!is_seed() || is_paused())
|
||||||
|
return m_seeding_time;
|
||||||
// m_seeding_time does not account for the current "session", just the
|
// m_seeding_time does not account for the current "session", just the
|
||||||
// time before we last started this torrent. To get the current time, we
|
// time before we last started this torrent. To get the current time, we
|
||||||
// need to add the time since we started it
|
// need to add the time since we started it
|
||||||
return m_seeding_time + ((!is_seed() || is_paused()) ? 0
|
return m_seeding_time + duration_cast<seconds32>(
|
||||||
: m_ses.session_time() - m_became_seed);
|
aux::time_now() - m_became_seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
seconds32 torrent::upload_mode_time() const
|
||||||
|
{
|
||||||
|
if(!m_upload_mode)
|
||||||
|
return seconds32(0);
|
||||||
|
|
||||||
|
return duration_cast<seconds32>(
|
||||||
|
aux::time_now() - m_upload_mode_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void torrent::second_tick(int tick_interval_ms)
|
void torrent::second_tick(int tick_interval_ms)
|
||||||
|
@ -8940,9 +8926,8 @@ namespace libtorrent
|
||||||
// if we're in upload only mode and we're auto-managed
|
// if we're in upload only mode and we're auto-managed
|
||||||
// leave upload mode every 10 minutes hoping that the error
|
// leave upload mode every 10 minutes hoping that the error
|
||||||
// condition has been fixed
|
// condition has been fixed
|
||||||
if (m_upload_mode && m_auto_managed
|
if (m_upload_mode && m_auto_managed && upload_mode_time() >=
|
||||||
&& int(m_ses.session_time() - m_upload_mode_time)
|
seconds(settings().get_int(settings_pack::optimistic_disk_retry)))
|
||||||
>= settings().get_int(settings_pack::optimistic_disk_retry))
|
|
||||||
{
|
{
|
||||||
set_upload_mode(false);
|
set_upload_mode(false);
|
||||||
}
|
}
|
||||||
|
@ -10580,22 +10565,23 @@ namespace libtorrent
|
||||||
|
|
||||||
// activity time
|
// activity time
|
||||||
#ifndef TORRENT_NO_DEPRECATE
|
#ifndef TORRENT_NO_DEPRECATE
|
||||||
st->finished_time = finished_time();
|
// cast to seconds in case that internal values doesn't have ratio<1>
|
||||||
st->active_time = active_time();
|
st->finished_time = int(duration_cast<seconds>(finished_time()).count());
|
||||||
st->seeding_time = seeding_time();
|
st->active_time = int(duration_cast<seconds>(active_time()).count());
|
||||||
|
st->seeding_time = int(duration_cast<seconds>(seeding_time()).count());
|
||||||
|
|
||||||
st->time_since_upload = m_last_upload == (std::numeric_limits<std::int16_t>::min)() ? -1
|
st->time_since_upload = int(total_seconds(aux::time_now()
|
||||||
: clamped_subtract_u16(m_ses.session_time(), m_last_upload);
|
- m_last_upload));
|
||||||
st->time_since_download = m_last_download == (std::numeric_limits<std::int16_t>::min)() ? -1
|
st->time_since_download = int(total_seconds(aux::time_now()
|
||||||
: clamped_subtract_u16(m_ses.session_time(), m_last_download);
|
- m_last_download));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
st->finished_duration = seconds{finished_time()};
|
st->finished_duration = finished_time();
|
||||||
st->active_duration = seconds{active_time()};
|
st->active_duration = active_time();
|
||||||
st->seeding_duration = seconds{seeding_time()};
|
st->seeding_duration = seeding_time();
|
||||||
|
|
||||||
st->last_upload = m_ses.session_start_time() + seconds(m_last_upload);
|
st->last_upload = m_last_upload;
|
||||||
st->last_download = m_ses.session_start_time() + seconds(m_last_download);
|
st->last_download = m_last_download;
|
||||||
|
|
||||||
st->storage_mode = static_cast<storage_mode_t>(m_storage_mode);
|
st->storage_mode = static_cast<storage_mode_t>(m_storage_mode);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue