merged RC_1_1 into master

This commit is contained in:
arvidn 2018-04-28 03:25:36 +02:00
commit 8621477239
18 changed files with 87 additions and 67 deletions

View File

@ -753,6 +753,8 @@ void bind_session()
.def_readwrite("added_time", &add_torrent_params::added_time) .def_readwrite("added_time", &add_torrent_params::added_time)
.def_readwrite("completed_time", &add_torrent_params::completed_time) .def_readwrite("completed_time", &add_torrent_params::completed_time)
.def_readwrite("last_seen_complete", &add_torrent_params::last_seen_complete) .def_readwrite("last_seen_complete", &add_torrent_params::last_seen_complete)
.def_readwrite("last_download", &add_torrent_params::last_download)
.def_readwrite("last_upload", &add_torrent_params::last_upload)
.def_readwrite("num_complete", &add_torrent_params::num_complete) .def_readwrite("num_complete", &add_torrent_params::num_complete)
.def_readwrite("num_incomplete", &add_torrent_params::num_incomplete) .def_readwrite("num_incomplete", &add_torrent_params::num_incomplete)
.def_readwrite("num_downloaded", &add_torrent_params::num_downloaded) .def_readwrite("num_downloaded", &add_torrent_params::num_downloaded)

View File

@ -167,9 +167,11 @@ class test_torrent_handle(unittest.TestCase):
sessionStart = datetime.datetime.now().replace(microsecond=0) sessionStart = datetime.datetime.now().replace(microsecond=0)
self.setup() self.setup()
st = self.h.status() st = self.h.status()
for attr in dir(st):
print('%s: %s' % (attr, getattr(st, attr)))
# last upload and download times are at session start time # last upload and download times are at session start time
self.assertLessEqual(abs(st.last_upload - sessionStart), datetime.timedelta(seconds=1)) self.assertEqual(st.last_upload, None)
self.assertLessEqual(abs(st.last_download - sessionStart), datetime.timedelta(seconds=1)) self.assertEqual(st.last_download, None)
def test_serialize_trackers(self): def test_serialize_trackers(self):
"""Test to ensure the dict contains only python built-in types""" """Test to ensure the dict contains only python built-in types"""

View File

@ -434,6 +434,12 @@ The file format is a bencoded dictionary containing the following fields:
| ``seeding_time`` | integer. The number of seconds this torrent has been active | | ``seeding_time`` | integer. The number of seconds this torrent has been active |
| | and seeding. | | | and seeding. |
+--------------------------+--------------------------------------------------------------+ +--------------------------+--------------------------------------------------------------+
| ``last_upload`` | integer. The number of seconds since epoch when we last |
| | uploaded payload to a peer on this torrent. |
+--------------------------+--------------------------------------------------------------+
| ``last_download`` | integer. The number of seconds since epoch when we last |
| | downloaded payload from a peer on this torrent. |
+--------------------------+--------------------------------------------------------------+
| ``upload_rate_limit`` | integer. In case this torrent has a per-torrent upload rate | | ``upload_rate_limit`` | integer. In case this torrent has a per-torrent upload rate |
| | limit, this is that limit. In bytes per second. | | | limit, this is that limit. In bytes per second. |
+--------------------------+--------------------------------------------------------------+ +--------------------------+--------------------------------------------------------------+

View File

@ -331,6 +331,9 @@ namespace libtorrent {
// applied before the torrent is added. // applied before the torrent is added.
aux::noexcept_movable<std::map<file_index_t, std::string>> renamed_files; aux::noexcept_movable<std::map<file_index_t, std::string>> renamed_files;
std::time_t last_download = 0;
std::time_t last_upload = 0;
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
// deprecated in 1.2 // deprecated in 1.2

View File

@ -195,7 +195,7 @@ namespace detail {
break; break;
case entry::preformatted_t: case entry::preformatted_t:
std::copy(e.preformatted().begin(), e.preformatted().end(), out); std::copy(e.preformatted().begin(), e.preformatted().end(), out);
ret += int(e.preformatted().size()); ret += static_cast<int>(e.preformatted().size());
break; break;
case entry::undefined_t: case entry::undefined_t:

View File

@ -442,7 +442,8 @@ namespace aux {
// used to convert dirty blocks into non-dirty ones // used to convert dirty blocks into non-dirty ones
// i.e. from being part of the write cache to being part // i.e. from being part of the write cache to being part
// of the read cache. it's used when flushing blocks to disk // of the read cache. it's used when flushing blocks to disk
void blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed); // returns true if the piece entry was freed
bool blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed);
// adds a block to the cache, marks it as dirty and // adds a block to the cache, marks it as dirty and
// associates the job with it. When the block is // associates the job with it. When the block is

View File

@ -450,7 +450,8 @@ namespace aux {
, span<iovec_t> iov, span<int> flushing, int block_base_index = 0); , span<iovec_t> iov, span<int> flushing, int block_base_index = 0);
void flush_iovec(cached_piece_entry* pe, span<iovec_t const> iov, span<int const> flushing void flush_iovec(cached_piece_entry* pe, span<iovec_t const> iov, span<int const> flushing
, int num_blocks, storage_error& error); , int num_blocks, storage_error& error);
void iovec_flushed(cached_piece_entry* pe // returns true if the piece entry was freed
bool iovec_flushed(cached_piece_entry* pe
, int* flushing, int num_blocks, int block_offset , int* flushing, int num_blocks, int block_offset
, storage_error const& error , storage_error const& error
, jobqueue_t& completed_jobs); , jobqueue_t& completed_jobs);

View File

@ -1620,9 +1620,8 @@ 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 // seconds since epoch.
// before the session started time_point32 m_last_download{seconds32(0)};
time_point32 m_last_download = aux::time_now32();
// 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()
@ -1633,9 +1632,8 @@ namespace libtorrent {
std::uint16_t m_num_connecting_seeds = 0; std::uint16_t m_num_connecting_seeds = 0;
// 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 // seconds since epoch.
// before the session started. time_point32 m_last_upload{seconds32(0)};
time_point32 m_last_upload = aux::time_now32();
// ---- // ----
@ -1667,7 +1665,7 @@ namespace libtorrent {
// the timestamp of the last scrape request to one of the trackers in // the timestamp of the last scrape request to one of the trackers in
// this torrent specified in session_time. This is signed because it must // this torrent specified in session_time. This is signed because it must
// be able to represent time before the session started // be able to represent time before the session started
std::int16_t m_last_scrape = (std::numeric_limits<std::int16_t>::min)(); time_point32 m_last_scrape{seconds32(0)};
#endif #endif
// ---- // ----

View File

@ -588,9 +588,7 @@ 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;

View File

@ -97,9 +97,9 @@ TORRENT_TEST(status_timers)
TEST_EQUAL(st.finished_duration.count(), since_finish.count()); TEST_EQUAL(st.finished_duration.count(), since_finish.count());
// does not upload without peers // does not upload without peers
TEST_CHECK(st.last_upload == start_time); TEST_CHECK(st.last_upload == time_point(seconds(0)));
// does not download in seeding mode // does not download in seeding mode
TEST_CHECK(st.last_download == start_time); TEST_CHECK(st.last_download == time_point(seconds(0)));
} }
return false; return false;
}); });
@ -110,7 +110,6 @@ TORRENT_TEST(status_timers_last_upload)
{ {
bool ran_to_completion = false; bool ran_to_completion = false;
lt::time_point32 start_time;
lt::torrent_handle handle; lt::torrent_handle handle;
setup_swarm(2, swarm_test::upload setup_swarm(2, swarm_test::upload
@ -123,13 +122,12 @@ TORRENT_TEST(status_timers_last_upload)
if (auto ta = alert_cast<add_torrent_alert>(a)) if (auto ta = alert_cast<add_torrent_alert>(a))
{ {
TEST_CHECK(!handle.is_valid()); TEST_CHECK(!handle.is_valid());
start_time = time_now();
handle = ta->handle; handle = ta->handle;
torrent_status st = handle.status(); torrent_status st = handle.status();
// test last upload and download state before wo go throgh // test last upload and download state before wo go throgh
// torrent states // torrent states
TEST_CHECK(st.last_download == start_time); TEST_CHECK(st.last_upload == time_point(seconds(0)));
TEST_CHECK(st.last_upload == start_time); TEST_CHECK(st.last_download == time_point(seconds(0)));
} }
} }
// terminate // terminate
@ -145,7 +143,7 @@ TORRENT_TEST(status_timers_last_upload)
// uploadtime is 0 seconds behind now // uploadtime is 0 seconds behind now
TEST_CHECK(eq(st.last_upload, time_now())); TEST_CHECK(eq(st.last_upload, time_now()));
// does not download in seeding mode // does not download in seeding mode
TEST_CHECK(eq(st.last_download, start_time)); TEST_CHECK(st.last_download == time_point(seconds(0)));
return false; return false;
}); });
TEST_CHECK(ran_to_completion); TEST_CHECK(ran_to_completion);
@ -155,7 +153,6 @@ TORRENT_TEST(status_timers_time_shift_with_active_torrent)
{ {
bool ran_to_completion = false; bool ran_to_completion = false;
lt::time_point32 start_time;
lt::torrent_handle handle; lt::torrent_handle handle;
seconds expected_active_duration = seconds(1); seconds expected_active_duration = seconds(1);
bool tick_is_in_active_range = false; bool tick_is_in_active_range = false;
@ -171,13 +168,12 @@ TORRENT_TEST(status_timers_time_shift_with_active_torrent)
if (auto ta = alert_cast<add_torrent_alert>(a)) if (auto ta = alert_cast<add_torrent_alert>(a))
{ {
TEST_CHECK(!handle.is_valid()); TEST_CHECK(!handle.is_valid());
start_time = time_now();
handle = ta->handle; handle = ta->handle;
torrent_status st = handle.status(); torrent_status st = handle.status();
// test last upload and download state before wo go throgh // test last upload and download state before wo go throgh
// torrent states // torrent states
TEST_CHECK(st.last_download == start_time); TEST_CHECK(st.last_download == time_point(seconds(0)));
TEST_CHECK(st.last_upload == start_time); TEST_CHECK(st.last_upload == time_point(seconds(0)));
} }
} }
// terminate // terminate
@ -224,9 +220,9 @@ TORRENT_TEST(status_timers_time_shift_with_active_torrent)
TEST_EQUAL(st.seeding_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()); TEST_EQUAL(st.finished_duration.count(), expected_active_duration.count());
// does not upload without peers // does not upload without peers
TEST_CHECK(st.last_upload == start_time); TEST_CHECK(st.last_upload == time_point(seconds(0)));
// does not download in seeding mode // does not download in seeding mode
TEST_CHECK(st.last_download == start_time); TEST_CHECK(st.last_download == time_point(seconds(0)));
} }
return false; return false;
}); });
@ -237,7 +233,6 @@ TORRENT_TEST(finish_time_shift_active)
{ {
bool ran_to_completion = false; bool ran_to_completion = false;
lt::time_point32 start_time;
lt::torrent_handle handle; lt::torrent_handle handle;
seconds expected_active_duration = seconds(1); seconds expected_active_duration = seconds(1);
bool tick_is_in_active_range = false; bool tick_is_in_active_range = false;
@ -252,13 +247,12 @@ TORRENT_TEST(finish_time_shift_active)
if (auto ta = alert_cast<add_torrent_alert>(a)) if (auto ta = alert_cast<add_torrent_alert>(a))
{ {
TEST_CHECK(!handle.is_valid()); TEST_CHECK(!handle.is_valid());
start_time = time_now();
handle = ta->handle; handle = ta->handle;
torrent_status st = handle.status(); torrent_status st = handle.status();
// test last upload and download state before wo go throgh // test last upload and download state before wo go throgh
// torrent states // torrent states
TEST_CHECK(st.last_download == start_time); TEST_CHECK(st.last_download == time_point(seconds(0)));
TEST_CHECK(st.last_upload == start_time); TEST_CHECK(st.last_upload == time_point(seconds(0)));
} }
} }
// terminate // terminate
@ -298,9 +292,9 @@ TORRENT_TEST(finish_time_shift_active)
TEST_EQUAL(st.seeding_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()); TEST_EQUAL(st.finished_duration.count(), expected_active_duration.count());
// does not upload without peers // does not upload without peers
TEST_CHECK(st.last_upload == start_time); TEST_CHECK(st.last_upload == time_point(seconds(0)));
// does not download in seeding mode // does not download in seeding mode
TEST_CHECK(st.last_download == start_time); TEST_CHECK(st.last_download == time_point(seconds(0)));
} }
return false; return false;
}); });
@ -311,7 +305,6 @@ TORRENT_TEST(finish_time_shift_paused)
{ {
bool ran_to_completion = false; bool ran_to_completion = false;
lt::time_point32 start_time;
lt::torrent_handle handle; lt::torrent_handle handle;
seconds expected_active_duration = seconds(1); seconds expected_active_duration = seconds(1);
bool tick_is_in_active_range = false; bool tick_is_in_active_range = false;
@ -326,13 +319,12 @@ TORRENT_TEST(finish_time_shift_paused)
if (auto ta = alert_cast<add_torrent_alert>(a)) if (auto ta = alert_cast<add_torrent_alert>(a))
{ {
TEST_CHECK(!handle.is_valid()); TEST_CHECK(!handle.is_valid());
start_time = time_now();
handle = ta->handle; handle = ta->handle;
torrent_status st = handle.status(); torrent_status st = handle.status();
// test last upload and download state before wo go throgh // test last upload and download state before wo go throgh
// torrent states // torrent states
TEST_CHECK(eq(st.last_download, start_time)); TEST_CHECK(st.last_upload == time_point(seconds(0)));
TEST_CHECK(eq(st.last_upload, start_time)); TEST_CHECK(st.last_download == time_point(seconds(0)));
} }
} }
// terminate // terminate
@ -374,9 +366,9 @@ TORRENT_TEST(finish_time_shift_paused)
TEST_EQUAL(st.seeding_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()); TEST_EQUAL(st.finished_duration.count(), expected_active_duration.count());
// does not upload without peers // does not upload without peers
TEST_CHECK(st.last_upload == start_time); TEST_CHECK(st.last_upload == time_point(seconds(0)));
// does not download in seeding mode // does not download in seeding mode
TEST_CHECK(st.last_download == start_time); TEST_CHECK(st.last_download == time_point(seconds(0)));
} }
return false; return false;
}); });

View File

@ -766,7 +766,7 @@ cached_piece_entry* block_cache::add_dirty_block(disk_io_job* j)
// (since these blocks now are part of the read cache) the refcounts of the // (since these blocks now are part of the read cache) the refcounts of the
// blocks are also decremented by this function. They are expected to have been // blocks are also decremented by this function. They are expected to have been
// incremented by the caller. // incremented by the caller.
void block_cache::blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed) bool block_cache::blocks_flushed(cached_piece_entry* pe, int const* flushed, int num_flushed)
{ {
TORRENT_PIECE_ASSERT(pe->in_use, pe); TORRENT_PIECE_ASSERT(pe->in_use, pe);
@ -790,7 +790,7 @@ void block_cache::blocks_flushed(cached_piece_entry* pe, int const* flushed, int
pe->num_dirty -= num_flushed; pe->num_dirty -= num_flushed;
update_cache_state(pe); update_cache_state(pe);
maybe_free_piece(pe); return maybe_free_piece(pe);
} }
std::pair<block_cache::const_iterator, block_cache::const_iterator> block_cache::all_pieces() const std::pair<block_cache::const_iterator, block_cache::const_iterator> block_cache::all_pieces() const

View File

@ -62,9 +62,10 @@ POSSIBILITY OF SUCH DAMAGE.
#define DEBUG_DISK_THREAD 0 #define DEBUG_DISK_THREAD 0
#if DEBUG_DISK_THREAD #if DEBUG_DISK_THREAD
#include <cstdarg> #include <cstdarg> // for va_list
#include <sstream> #include <sstream>
#include <cstdio> // for vsnprintf #include <cstdio> // for vsnprintf
#define DLOG(...) debug_log(__VA_ARGS__) #define DLOG(...) debug_log(__VA_ARGS__)
#else #else
#define DLOG(...) do {} while(false) #define DLOG(...) do {} while(false)
@ -727,7 +728,7 @@ constexpr disk_job_flags_t disk_interface::cache_hit;
// It is necessary to call this function with the blocks produced by // It is necessary to call this function with the blocks produced by
// build_iovec, to reset their state to not being flushed anymore // build_iovec, to reset their state to not being flushed anymore
// the cache needs to be locked when calling this function // the cache needs to be locked when calling this function
void disk_io_thread::iovec_flushed(cached_piece_entry* pe bool disk_io_thread::iovec_flushed(cached_piece_entry* pe
, int* flushing, int const num_blocks, int const block_offset , int* flushing, int const num_blocks, int const block_offset
, storage_error const& error , storage_error const& error
, jobqueue_t& completed_jobs) , jobqueue_t& completed_jobs)
@ -742,7 +743,8 @@ constexpr disk_job_flags_t disk_interface::cache_hit;
DLOG("%d ", flushing[i]); DLOG("%d ", flushing[i]);
DLOG("]\n"); DLOG("]\n");
#endif #endif
m_disk_cache.blocks_flushed(pe, flushing, num_blocks); if (m_disk_cache.blocks_flushed(pe, flushing, num_blocks))
return true;
if (error) if (error)
{ {
@ -770,6 +772,8 @@ constexpr disk_job_flags_t disk_interface::cache_hit;
j = next; j = next;
} }
} }
return false;
} }
// issues write operations for blocks in the given // issues write operations for blocks in the given
@ -802,9 +806,8 @@ constexpr disk_job_flags_t disk_interface::cache_hit;
flush_iovec(pe, iov, flushing, iov_len, error); flush_iovec(pe, iov, flushing, iov_len, error);
} }
iovec_flushed(pe, flushing.data(), iov_len, 0, error, completed_jobs); if (!iovec_flushed(pe, flushing.data(), iov_len, 0, error, completed_jobs))
m_disk_cache.maybe_free_piece(pe);
m_disk_cache.maybe_free_piece(pe);
// if the cache is under high pressure, we need to evict // if the cache is under high pressure, we need to evict
// the blocks we just flushed to make room for more write pieces // the blocks we just flushed to make room for more write pieces

View File

@ -121,6 +121,9 @@ namespace {
ret.last_seen_complete = std::time_t(rd.dict_find_int_value("last_seen_complete")); ret.last_seen_complete = std::time_t(rd.dict_find_int_value("last_seen_complete"));
ret.last_download = rd.dict_find_int_value("last_download", 0);
ret.last_upload = rd.dict_find_int_value("last_upload", 0);
// scrape data cache // scrape data cache
ret.num_complete = int(rd.dict_find_int_value("num_complete", -1)); ret.num_complete = int(rd.dict_find_int_value("num_complete", -1));
ret.num_incomplete = int(rd.dict_find_int_value("num_incomplete", -1)); ret.num_incomplete = int(rd.dict_find_int_value("num_incomplete", -1));

View File

@ -308,6 +308,8 @@ namespace {
atp.seeding_time = resume_data.seeding_time; atp.seeding_time = resume_data.seeding_time;
atp.last_seen_complete = resume_data.last_seen_complete; atp.last_seen_complete = resume_data.last_seen_complete;
atp.last_upload = resume_data.last_upload;
atp.last_download = resume_data.last_download;
atp.url = resume_data.url; atp.url = resume_data.url;
atp.uuid = resume_data.uuid; atp.uuid = resume_data.uuid;

View File

@ -97,6 +97,8 @@ POSSIBILITY OF SUCH DAMAGE.
// for logging stat layout // for logging stat layout
#include "libtorrent/stat.hpp" #include "libtorrent/stat.hpp"
#include <cstdarg> // for va_list
// for logging the size of DHT structures // for logging the size of DHT structures
#ifndef TORRENT_DISABLE_DHT #ifndef TORRENT_DISABLE_DHT
#include <libtorrent/kademlia/find_data.hpp> #include <libtorrent/kademlia/find_data.hpp>

View File

@ -181,6 +181,8 @@ bool is_downloading_state(int const st)
, bool const session_paused , bool const session_paused
, add_torrent_params const& p) , add_torrent_params const& p)
: torrent_hot_members(ses, p, session_paused) : torrent_hot_members(ses, p, session_paused)
, m_total_uploaded(p.total_uploaded)
, m_total_downloaded(p.total_downloaded)
, m_tracker_timer(ses.get_io_service()) , m_tracker_timer(ses.get_io_service())
, m_inactivity_timer(ses.get_io_service()) , m_inactivity_timer(ses.get_io_service())
, m_trackerid(p.trackerid) , m_trackerid(p.trackerid)
@ -224,6 +226,8 @@ bool is_downloading_state(int const st)
, m_announce_to_dht(!(p.flags & torrent_flags::paused)) , m_announce_to_dht(!(p.flags & torrent_flags::paused))
, m_ssl_torrent(false) , m_ssl_torrent(false)
, m_deleted(false) , m_deleted(false)
, m_last_download(seconds32(p.last_download))
, m_last_upload(seconds32(p.last_upload))
, m_auto_managed(p.flags & torrent_flags::auto_managed) , m_auto_managed(p.flags & torrent_flags::auto_managed)
, m_current_gauge_state(static_cast<std::uint32_t>(no_gauge_state)) , m_current_gauge_state(static_cast<std::uint32_t>(no_gauge_state))
, m_moving_storage(false) , m_moving_storage(false)
@ -2968,7 +2972,7 @@ bool is_downloading_state(int const st)
{ {
TORRENT_ASSERT(is_single_thread()); TORRENT_ASSERT(is_single_thread());
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
m_last_scrape = std::int16_t(m_ses.session_time()); m_last_scrape = aux::time_now32();
#endif #endif
if (m_trackers.empty()) return; if (m_trackers.empty()) return;
@ -3151,7 +3155,7 @@ bool is_downloading_state(int const st)
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
if (resp.complete >= 0 && resp.incomplete >= 0) if (resp.complete >= 0 && resp.incomplete >= 0)
m_last_scrape = std::int16_t(m_ses.session_time()); m_last_scrape = aux::time_now32();
#endif #endif
#ifndef TORRENT_DISABLE_LOGGING #ifndef TORRENT_DISABLE_LOGGING
@ -6110,6 +6114,8 @@ bool is_downloading_state(int const st)
ret.finished_time = static_cast<int>(total_seconds(finished_time())); ret.finished_time = static_cast<int>(total_seconds(finished_time()));
ret.seeding_time = static_cast<int>(total_seconds(seeding_time())); ret.seeding_time = static_cast<int>(total_seconds(seeding_time()));
ret.last_seen_complete = m_last_seen_complete; ret.last_seen_complete = m_last_seen_complete;
ret.last_upload = total_seconds(m_last_upload.time_since_epoch());
ret.last_download = total_seconds(m_last_download.time_since_epoch());
ret.num_complete = m_complete; ret.num_complete = m_complete;
ret.num_incomplete = m_incomplete; ret.num_incomplete = m_incomplete;
@ -8296,14 +8302,7 @@ bool is_downloading_state(int const st)
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)
{
if (a + std::numeric_limits<std::int16_t>::min() < b)
return std::numeric_limits<std::int16_t>::min();
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
@ -8324,10 +8323,6 @@ bool is_downloading_state(int const st)
pe->last_connected = clamped_subtract_u16(pe->last_connected, seconds); pe->last_connected = clamped_subtract_u16(pe->last_connected, seconds);
} }
} }
#ifndef TORRENT_NO_DEPRECATE
m_last_scrape = clamped_subtract_s16(m_last_scrape, seconds);
#endif
} }
// the higher seed rank, the more important to seed // the higher seed rank, the more important to seed
@ -10637,8 +10632,7 @@ bool is_downloading_state(int const st)
st->completed_time = m_completed_time; st->completed_time = m_completed_time;
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
st->last_scrape = m_last_scrape == std::numeric_limits<std::int16_t>::min() ? -1 st->last_scrape = static_cast<int>(total_seconds(aux::time_now32() - m_last_scrape));
: clamped_subtract_u16(m_ses.session_time(), m_last_scrape);
#endif #endif
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
@ -10667,10 +10661,12 @@ bool is_downloading_state(int const st)
st->active_time = int(total_seconds(active_time())); st->active_time = int(total_seconds(active_time()));
st->seeding_time = int(total_seconds(seeding_time())); st->seeding_time = int(total_seconds(seeding_time()));
st->time_since_upload = int(total_seconds(aux::time_now() time_point32 const unset{seconds32(0)};
- m_last_upload));
st->time_since_download = int(total_seconds(aux::time_now() st->time_since_upload = m_last_upload == unset ? -1
- m_last_download)); : static_cast<int>(total_seconds(aux::time_now32() - m_last_upload));
st->time_since_download = m_last_download == unset ? -1
: static_cast<int>(total_seconds(aux::time_now32() - m_last_download));
#endif #endif
st->finished_duration = finished_time(); st->finished_duration = finished_time();

View File

@ -63,6 +63,8 @@ namespace libtorrent {
ret["finished_time"] = atp.finished_time; ret["finished_time"] = atp.finished_time;
ret["seeding_time"] = atp.seeding_time; ret["seeding_time"] = atp.seeding_time;
ret["last_seen_complete"] = atp.last_seen_complete; ret["last_seen_complete"] = atp.last_seen_complete;
ret["last_download"] = atp.last_download;
ret["last_upload"] = atp.last_upload;
ret["num_complete"] = atp.num_complete; ret["num_complete"] = atp.num_complete;
ret["num_incomplete"] = atp.num_incomplete; ret["num_incomplete"] = atp.num_incomplete;

View File

@ -111,6 +111,8 @@ std::vector<char> generate_resume_data(torrent_info* ti
rd["super_seeding"] = 0; rd["super_seeding"] = 0;
rd["added_time"] = 1347; rd["added_time"] = 1347;
rd["completed_time"] = 1348; rd["completed_time"] = 1348;
rd["last_download"] = 2;
rd["last_upload"] = 3;
rd["finished_time"] = 1352; rd["finished_time"] = 1352;
if (file_priorities && file_priorities[0]) if (file_priorities && file_priorities[0])
{ {
@ -208,6 +210,13 @@ void default_tests(torrent_status const& s)
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
TEST_CHECK(s.active_time >= 1339); TEST_CHECK(s.active_time >= 1339);
TEST_CHECK(s.active_time < 1339 + 10); TEST_CHECK(s.active_time < 1339 + 10);
auto const now = duration_cast<seconds>(clock_type::now().time_since_epoch()).count();
TEST_CHECK(s.time_since_download >= now - 2);
TEST_CHECK(s.time_since_upload >= now - 3);
TEST_CHECK(s.time_since_download < now - 2 + 10);
TEST_CHECK(s.time_since_upload < now - 3 + 10);
#endif #endif
using lt::seconds; using lt::seconds;