diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 94312c3b8..2dedda1c3 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -447,6 +447,10 @@ namespace libtorrent void do_pause(); void do_resume(); + int finished_time() const; + int active_time() const; + int seeding_time() const; + bool is_paused() const; bool allows_peers() const { return m_allow_peers; } bool is_torrent_paused() const { return !m_allow_peers || m_graceful_pause_mode; } @@ -686,7 +690,7 @@ namespace libtorrent void announce_with_tracker(boost::uint8_t e = tracker_request::none , address const& bind_interface = address_v4::any()); - int seconds_since_last_scrape() const { return m_last_scrape; } + int seconds_since_last_scrape() const { return m_ses.session_time() - m_last_scrape; } #ifndef TORRENT_DISABLE_DHT void dht_announce(); @@ -1028,7 +1032,7 @@ namespace libtorrent // that are not private void lsd_announce(); - void update_last_upload() { m_last_upload = 0; } + void update_last_upload() { m_last_upload = m_ses.session_time(); } void set_apply_ip_filter(bool b); bool apply_ip_filter() const { return m_apply_ip_filter; } @@ -1307,6 +1311,14 @@ namespace libtorrent // hours to keep the timestamps fit in 16 bits boost::uint16_t m_started; + // if we're a seed, this is the session time + // timestamp of when we became one + boost::uint16_t m_became_seed; + + // if we're finished, this is the session time + // timestamp of when we finished + boost::uint16_t m_became_finished; + // when checking, this is the first piece we have not // issued a hash job for int m_checking_piece; @@ -1357,8 +1369,9 @@ namespace libtorrent // 8 bits after each one // ============================== - // the number of seconds we've been in upload mode - unsigned int m_upload_mode_time:24; + // the session time timestamp of when we entered upload mode + // if we're currently in upload-mode + boost::uint16_t m_upload_mode_time; // true when this torrent should anncounce to // trackers @@ -1551,9 +1564,11 @@ namespace libtorrent // ---- - // the number of seconds since the last piece passed for - // this torrent - boost::uint64_t m_last_download:24; + // the timestamp of the last piece passed for this torrent + // specified in session_time + boost::uint16_t m_last_download; + + // TODO: 8 bits free here // this is a second count-down to when we should tick the // storage for this torrent. Ticking the storage is used @@ -1565,9 +1580,11 @@ namespace libtorrent // ---- - // the number of seconds since the last byte was uploaded - // from this torrent - boost::uint64_t m_last_upload:24; + // the timestamp of the last byte uploaded from this torrent + // specified in session_time + boost::uint16_t m_last_upload; + + // TODO: 8 bits here // if this is true, libtorrent may pause and resume // this torrent depending on queuing rules. Torrents @@ -1596,9 +1613,10 @@ namespace libtorrent // is optional and may be 0xffffff unsigned int m_downloaded:24; - // the number of seconds since the last scrape request to + // the timestamp of the last scrape request to // one of the trackers in this torrent - boost::uint64_t m_last_scrape:16; + // specified in session_time + boost::uint16_t m_last_scrape; // ---- diff --git a/src/torrent.cpp b/src/torrent.cpp index fe1cabdf1..aac94c980 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -167,6 +167,8 @@ namespace libtorrent , m_num_verified(0) , m_last_saved_resume(ses.session_time()) , m_started(ses.session_time()) + , m_became_seed(0) + , m_became_finished(0) , m_checking_piece(0) , m_num_checked_pieces(0) , m_refcount(0) @@ -1023,7 +1025,7 @@ namespace libtorrent p->cancel_all_requests(); } // this is used to try leaving upload only mode periodically - m_upload_mode_time = 0; + m_upload_mode_time = m_ses.session_time(); } else if (m_policy) { @@ -2758,7 +2760,7 @@ namespace libtorrent if (i->verified) ++verified_trackers; if (verified_trackers > 0) - debug_log("DHT: only using DHT as callback, and there are %d working trackers", verified_trackers); + debug_log("DHT: only using DHT as fallback, and there are %d working trackers", verified_trackers); } #endif return; @@ -3033,7 +3035,7 @@ namespace libtorrent void torrent::scrape_tracker() { TORRENT_ASSERT(m_ses.is_single_thread()); - m_last_scrape = 0; + m_last_scrape = m_ses.session_time(); if (m_trackers.empty()) return; @@ -3169,7 +3171,7 @@ namespace libtorrent update_tracker_timer(now); if (complete >= 0 && incomplete >= 0) - m_last_scrape = 0; + m_last_scrape = m_ses.session_time(); #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING debug_log("TRACKER RESPONSE\n" @@ -4023,7 +4025,7 @@ namespace libtorrent // is deallocated by the torrent once it starts seeding } - m_last_download = 0; + m_last_download = m_ses.session_time(); if (m_share_mode) recalc_share_mode(); @@ -6455,10 +6457,6 @@ namespace libtorrent } super_seeding(rd.dict_find_int_value("super_seeding", 0)); - m_last_scrape = rd.dict_find_int_value("last_scrape", 0); - m_last_download = rd.dict_find_int_value("last_download", 0); - m_last_upload = rd.dict_find_int_value("last_upload", 0); - if (!m_use_resume_save_path) { std::string p = rd.dict_find_string_value("save_path"); @@ -6674,9 +6672,9 @@ namespace libtorrent ret["total_uploaded"] = m_total_uploaded; ret["total_downloaded"] = m_total_downloaded; - ret["active_time"] = m_active_time; - ret["finished_time"] = m_finished_time; - ret["seeding_time"] = m_seeding_time; + ret["active_time"] = active_time(); + ret["finished_time"] = finished_time(); + ret["seeding_time"] = seeding_time(); ret["last_seen_complete"] = m_last_seen_complete; ret["num_complete"] = m_complete; @@ -6691,10 +6689,6 @@ namespace libtorrent ret["added_time"] = m_added_time; ret["completed_time"] = m_completed_time; - ret["last_scrape"] = m_last_scrape; - ret["last_download"] = m_last_download; - ret["last_upload"] = m_last_upload; - ret["save_path"] = m_save_path; if (!m_url.empty()) ret["url"] = m_url; @@ -7828,6 +7822,8 @@ namespace libtorrent set_state(torrent_status::finished); set_queue_position(-1); + m_became_finished = m_ses.session_time(); + // we have to call completed() before we start // disconnecting peers, since there's an assert // to make sure we're cleared the piece picker @@ -7939,6 +7935,8 @@ namespace libtorrent maybe_done_flushing(); set_state(torrent_status::seeding); + m_became_seed = m_ses.session_time(); + // no need for this anymore std::vector().swap(m_file_progress); if (!m_announcing) return; @@ -8672,6 +8670,19 @@ namespace libtorrent } } + int clamped_subtract(int a, int b) + { + if (a < b) return 0; + return a - b; + } + + // this is called every time the session timer takes a step back. Since the + // session time is meant to fit in 16 bits, it only covers a range of + // about 18 hours. This means every few hours the whole epoch of this + // clock is shifted forward. All timestamp in this clock must then be + // shifted backwards to remain the same. Anything that's shifted back + // beyond the new epoch is clamped to 0 (to represent the oldest timestamp + // currently representable by the session_time) void torrent::step_session_time(int seconds) { if (m_policy) @@ -8681,22 +8692,36 @@ namespace libtorrent { torrent_peer* pe = *j; - if (pe->last_optimistically_unchoked < seconds) - pe->last_optimistically_unchoked = 0; - else - pe->last_optimistically_unchoked -= seconds; - - if (pe->last_connected < seconds) - pe->last_connected = 0; - else - pe->last_connected -= seconds; + pe->last_optimistically_unchoked + = clamped_subtract(pe->last_optimistically_unchoked, seconds); + pe->last_connected = clamped_subtract(pe->last_connected, seconds); } } - if (m_started < seconds) m_started = 0; - else m_started -= seconds; - if (m_last_saved_resume < seconds) m_last_saved_resume = 0; - else m_last_saved_resume -= seconds; + if (m_started < seconds) + { + // the started time just got shifted out of the valid window of + // session time. Record this "lost time" by incrementing the + // counters that are supposed to keep track of the total time we've + // been in certain states + int lost_seconds = m_started - seconds; + if (!is_paused()) + m_active_time += lost_seconds; + + if (is_seed()) + m_seeding_time += lost_seconds; + + if (is_finished()) + m_finished_time += lost_seconds; + } + + m_started = clamped_subtract(m_started, seconds); + + m_last_upload = clamped_subtract(m_last_upload, seconds); + m_last_download = clamped_subtract(m_last_download, seconds); + m_last_scrape = clamped_subtract(m_last_scrape, seconds); + m_last_saved_resume = clamped_subtract(m_last_saved_resume, seconds); + m_upload_mode_time = clamped_subtract(m_upload_mode_time, seconds); } // the higher seed rank, the more important to seed @@ -8718,16 +8743,16 @@ namespace libtorrent int ret = 0; - size_type finished_time = m_finished_time; - size_type download_time = int(m_active_time) - finished_time; + size_type fin_time = finished_time(); + size_type download_time = int(active_time()) - fin_time; // if we haven't yet met the seed limits, set the seed_ratio_not_met // flag. That will make this seed prioritized // downloaded may be 0 if the torrent is 0-sized size_type downloaded = (std::max)(m_total_downloaded, m_torrent_file->total_size()); - if (finished_time < s.get_int(settings_pack::seed_time_limit) + if (fin_time < s.get_int(settings_pack::seed_time_limit) && (download_time > 1 - && finished_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 && m_total_uploaded * 100 / downloaded < s.get_int(settings_pack::share_ratio_limit)) ret |= seed_ratio_not_met; @@ -8931,8 +8956,17 @@ namespace libtorrent } #endif + m_need_connect_boost = true; m_inactive = false; + m_active_time += m_ses.session_time() - m_started; + + if (is_seed()) + m_seeding_time += m_ses.session_time() - m_became_seed; + + if (is_finished()) + m_finished_time += m_ses.session_time() - m_became_finished; + state_updated(); update_want_peers(); update_want_scrape(); @@ -9136,6 +9170,9 @@ namespace libtorrent alerts().post_alert(torrent_resumed_alert(get_handle())); m_started = m_ses.session_time(); + if (is_seed()) m_became_seed = m_started; + if (is_finished()) m_became_finished = m_started; + clear_error(); state_updated(); @@ -9144,6 +9181,8 @@ namespace libtorrent update_want_scrape(); start_announcing(); + + do_connect_boost(); } void torrent::update_tracker_timer(ptime now) @@ -9307,7 +9346,25 @@ namespace libtorrent announce_with_tracker(tracker_request::stopped); } - void torrent::second_tick(int tick_interval_ms, int residual) + int torrent::finished_time() const + { + return m_finished_time + ((!is_finished() || is_paused()) ? 0 + : (m_ses.session_time() - m_became_finished)); + } + + int torrent::active_time() const + { + return m_active_time + (is_paused() ? 0 + : m_ses.session_time() - m_started); + } + + int torrent::seeding_time() const + { + return m_seeding_time + ((!is_seed() || is_paused()) ? 0 + : m_ses.session_time() - m_became_seed); + } + + void torrent::second_tick(int tick_interval_ms, int /* residual */) { TORRENT_ASSERT(want_tick()); TORRENT_ASSERT(m_ses.is_single_thread()); @@ -9330,7 +9387,8 @@ namespace libtorrent // if we're in upload only mode and we're auto-managed // leave upload mode every 10 minutes hoping that the error // condition has been fixed - if (m_upload_mode && m_auto_managed && int(m_upload_mode_time) + if (m_upload_mode && m_auto_managed + && int(m_ses.session_time() - m_upload_mode_time) >= settings().get_int(settings_pack::optimistic_disk_retry)) { set_upload_mode(false); @@ -9405,16 +9463,6 @@ namespace libtorrent } } - int seconds_since_last_tick = 1 + residual; - - if (is_seed()) m_seeding_time += seconds_since_last_tick; - if (is_finished()) m_finished_time += seconds_since_last_tick; - if (m_upload_mode) m_upload_mode_time += seconds_since_last_tick; - m_last_scrape += seconds_since_last_tick; - m_active_time += seconds_since_last_tick; - m_last_download += seconds_since_last_tick; - m_last_upload += seconds_since_last_tick; - // ---- TIME CRITICAL PIECES ---- #if TORRENT_DEBUG_STREAMING > 0 @@ -9499,6 +9547,8 @@ namespace libtorrent if (m_stat.low_pass_upload_rate() > 0 || m_stat.low_pass_download_rate() > 0) state_updated(); + // TODO: 4 this logic doesn't work for seeding torrents that are not ticked + // this section determines whether the torrent is active or not. When it // changes state, it may also trigger the auto-manage logic to reconsider // which torrents should be queued and started. There is a low pass @@ -11035,7 +11085,9 @@ namespace libtorrent st->added_time = m_added_time; st->completed_time = m_completed_time; - st->last_scrape = m_last_scrape; + st->last_scrape = m_last_scrape == 0 ? -1 + : m_ses.session_time() - m_last_scrape; + st->share_mode = m_share_mode; st->upload_mode = m_upload_mode; st->up_bandwidth_queue = 0; @@ -11062,11 +11114,13 @@ namespace libtorrent st->all_time_download = m_total_downloaded; // activity time - st->finished_time = m_finished_time; - st->active_time = m_active_time; - st->seeding_time = m_seeding_time; - st->time_since_upload = m_last_upload; - st->time_since_download = m_last_download; + st->finished_time = finished_time(); + st->active_time = active_time(); + st->seeding_time = seeding_time(); + st->time_since_upload = m_last_upload == 0 ? -1 + : m_ses.session_time() - m_last_upload; + st->time_since_download = m_last_download == 0 ? -1 + : m_ses.session_time() - m_last_download; st->storage_mode = (storage_mode_t)m_storage_mode;