diff --git a/libtorrent/bindings/python/src/torrent_handle.cpp b/libtorrent/bindings/python/src/torrent_handle.cpp index 9980e940f..343ef27bb 100755 --- a/libtorrent/bindings/python/src/torrent_handle.cpp +++ b/libtorrent/bindings/python/src/torrent_handle.cpp @@ -74,7 +74,7 @@ namespace list file_progress(torrent_handle& handle) { - std::vector p; + std::vector p; { allow_threading_guard guard; @@ -84,7 +84,7 @@ list file_progress(torrent_handle& handle) list result; - for (std::vector::iterator i(p.begin()), e(p.end()); i != e; ++i) + for (std::vector::iterator i(p.begin()), e(p.end()); i != e; ++i) result.append(*i); return result; @@ -267,6 +267,7 @@ void bind_torrent_handle() .def("is_paused", _(&torrent_handle::is_paused)) .def("pause", _(&torrent_handle::pause)) .def("resume", _(&torrent_handle::resume)) + .def("clear_error", _(&torrent_handle::clear_error)) .def("is_auto_managed", _(&torrent_handle::is_auto_managed)) .def("auto_managed", _(&torrent_handle::auto_managed)) diff --git a/libtorrent/include/libtorrent/alert_types.hpp b/libtorrent/include/libtorrent/alert_types.hpp index a1c556d82..88ce8f0dd 100644 --- a/libtorrent/include/libtorrent/alert_types.hpp +++ b/libtorrent/include/libtorrent/alert_types.hpp @@ -85,7 +85,7 @@ namespace libtorrent , std::string const& url_) : torrent_alert(h) , url(url_) - { assert(!url.empty()); } + {} const static int static_category = alert::tracker_notification; virtual int category() const { return static_category; } @@ -191,7 +191,7 @@ namespace libtorrent , times_in_row(times) , status_code(status) , msg(msg_) - {} + { TORRENT_ASSERT(!url.empty()); } virtual std::auto_ptr clone() const { return std::auto_ptr(new tracker_error_alert(*this)); } @@ -218,7 +218,7 @@ namespace libtorrent , std::string const& msg_) : tracker_alert(h, url) , msg(msg_) - {} + { TORRENT_ASSERT(!url.empty()); } std::string msg; @@ -242,7 +242,7 @@ namespace libtorrent : tracker_alert(h, url) , incomplete(incomplete_) , complete(complete_) - {} + { TORRENT_ASSERT(!url.empty()); } int incomplete; int complete; @@ -265,7 +265,7 @@ namespace libtorrent , std::string const& msg_) : tracker_alert(h, url) , msg(msg_) - {} + { TORRENT_ASSERT(!url.empty()); } std::string msg; @@ -287,7 +287,7 @@ namespace libtorrent , std::string const& url) : tracker_alert(h, url) , num_peers(np) - {} + { TORRENT_ASSERT(!url.empty()); } int num_peers; @@ -324,16 +324,20 @@ namespace libtorrent struct TORRENT_EXPORT tracker_announce_alert: tracker_alert { tracker_announce_alert(torrent_handle const& h - , std::string const& url) + , std::string const& url, int event_) : tracker_alert(h, url) - {} + , event(event_) + { TORRENT_ASSERT(!url.empty()); } + + int event; virtual std::auto_ptr clone() const { return std::auto_ptr(new tracker_announce_alert(*this)); } virtual char const* what() const { return "tracker announce sent"; } virtual std::string message() const { - return tracker_alert::message() + " sending announce"; + const static char* event_str[] = {"none", "completed", "started", "stopped"}; + return tracker_alert::message() + " sending announce (" + event_str[event] + ")"; } }; diff --git a/libtorrent/include/libtorrent/aux_/session_impl.hpp b/libtorrent/include/libtorrent/aux_/session_impl.hpp index 57da44b4a..e1345fab6 100644 --- a/libtorrent/include/libtorrent/aux_/session_impl.hpp +++ b/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -319,6 +319,18 @@ namespace libtorrent int next_port(); + void add_redundant_bytes(size_type b) + { + TORRENT_ASSERT(b > 0); + m_total_redundant_bytes += b; + } + + void add_failed_bytes(size_type b) + { + TORRENT_ASSERT(b > 0); + m_total_failed_bytes += b; + } + // handles delayed alerts alert_manager m_alerts; @@ -608,6 +620,10 @@ namespace libtorrent std::map m_as_peak; #endif + // total redundant and failed bytes + size_type m_total_failed_bytes; + size_type m_total_redundant_bytes; + // the main working thread boost::scoped_ptr m_thread; }; diff --git a/libtorrent/include/libtorrent/disk_io_thread.hpp b/libtorrent/include/libtorrent/disk_io_thread.hpp index 3556c3cf6..d0d574411 100644 --- a/libtorrent/include/libtorrent/disk_io_thread.hpp +++ b/libtorrent/include/libtorrent/disk_io_thread.hpp @@ -58,6 +58,8 @@ namespace libtorrent int piece; std::vector blocks; ptime last_use; + enum kind_t { read_cache = 0, write_cache = 1 }; + kind_t kind; }; struct disk_io_job diff --git a/libtorrent/include/libtorrent/session_settings.hpp b/libtorrent/include/libtorrent/session_settings.hpp index c7aee4a0f..fb98ad98b 100644 --- a/libtorrent/include/libtorrent/session_settings.hpp +++ b/libtorrent/include/libtorrent/session_settings.hpp @@ -87,8 +87,8 @@ namespace libtorrent , tracker_receive_timeout(40) , stop_tracker_timeout(5) , tracker_maximum_response_length(1024*1024) - , piece_timeout(10) - , request_timeout(40) + , piece_timeout(20) + , request_timeout(50) , request_queue_time(3.f) , max_allowed_in_request_queue(250) , max_out_request_queue(200) @@ -121,6 +121,7 @@ namespace libtorrent , upnp_ignore_nonrouters(true) , send_buffer_watermark(80 * 1024) , auto_upload_slots(true) + , use_parole_mode(true) , cache_size(512) , cache_expiry(60) , outgoing_ports(0,0) @@ -342,6 +343,13 @@ namespace libtorrent // the manual settings, through max_uploads. bool auto_upload_slots; + // if set to true, peers that participate in a failing + // piece is put in parole mode. i.e. They will only + // download whole pieces until they either fail or pass. + // they are taken out of parole mode as soon as they + // participate in a piece that passes. + bool use_parole_mode; + // the disk write cache, specified in 16 KiB blocks. // default is 512 (= 8 MB) int cache_size; diff --git a/libtorrent/include/libtorrent/session_status.hpp b/libtorrent/include/libtorrent/session_status.hpp index 4e42dba5f..6f74f5ff4 100644 --- a/libtorrent/include/libtorrent/session_status.hpp +++ b/libtorrent/include/libtorrent/session_status.hpp @@ -53,6 +53,9 @@ namespace libtorrent size_type total_payload_download; size_type total_payload_upload; + size_type total_redundant_bytes; + size_type total_failed_bytes; + int num_peers; int num_unchoked; int allowed_upload_slots; diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index b1c2cc541..46c438208 100644 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -156,6 +156,9 @@ namespace libtorrent void files_checked(); void start_checking(); + void start_announcing(); + void stop_announcing(); + int seed_rank(session_settings const& s) const; storage_mode_t storage_mode() const { return m_storage_mode; } @@ -169,6 +172,8 @@ namespace libtorrent torrent_status::state_t state() const { return m_state; } void set_state(torrent_status::state_t s); + void clear_error(); + session_settings const& settings() const; aux::session_impl& session() { return m_ses; } @@ -219,6 +224,7 @@ namespace libtorrent bool is_piece_filtered(int index) const; void filtered_pieces(std::vector& bitmask) const; void filter_files(std::vector const& files); + void file_progress(std::vector& fp) const; // ============ end deprecation ============= void piece_availability(std::vector& avail) const; @@ -232,7 +238,8 @@ namespace libtorrent void prioritize_files(std::vector const& files); torrent_status status() const; - void file_progress(std::vector& fp) const; + + void file_progress(std::vector& fp) const; void use_interface(const char* net_interface); tcp::endpoint const& get_interface() const { return m_net_interface; } @@ -354,10 +361,6 @@ namespace libtorrent virtual void tracker_scrape_response(tracker_request const& req , int complete, int incomplete, int downloaded); - // generates a request string for sending - // to the tracker - tracker_request generate_tracker_request(); - // if no password and username is set // this will return an empty string, otherwise // it will concatenate the login and password @@ -369,14 +372,12 @@ namespace libtorrent // announce will take place. ptime next_announce() const; - // returns true if it is time for this torrent to make another - // tracker request - bool should_request(); - // forcefully sets next_announce to the current time void force_tracker_request(); void force_tracker_request(ptime); void scrape_tracker(); + void announce_with_tracker(tracker_request::event_t e + = tracker_request::none); ptime const& last_scrape() const { return m_last_scrape; } // sets the username and password that will be sent to @@ -524,8 +525,8 @@ namespace libtorrent // this is done when a piece fails void restore_piece_state(int index); - void received_redundant_data(int num_bytes) - { TORRENT_ASSERT(num_bytes > 0); m_total_redundant_bytes += num_bytes; } + void add_redundant_bytes(int b); + void add_failed_bytes(int b); // this is true if we have all the pieces bool is_seed() const @@ -631,7 +632,7 @@ namespace libtorrent void on_piece_verified(int ret, disk_io_job const& j , boost::function f); - void try_next_tracker(); + void try_next_tracker(tracker_request const& req); int prioritize_tracker(int tracker_index); void on_country_lookup(error_code const& error, tcp::resolver::iterator i , boost::intrusive_ptr p) const; @@ -666,8 +667,6 @@ namespace libtorrent boost::intrusive_ptr m_torrent_file; - tracker_request::event_t m_event; - void parse_response(const entry& e, std::vector& peer_list); // if this pointer is 0, the torrent is in @@ -695,8 +694,8 @@ namespace libtorrent // the object. piece_manager* m_storage; - // the time of next tracker request - ptime m_next_request; + // the time of next tracker announce + ptime m_next_tracker_announce; #ifndef NDEBUG public: @@ -729,13 +728,24 @@ namespace libtorrent // this announce timer is used both // by Local service discovery and // by the DHT. - deadline_timer m_announce_timer; + deadline_timer m_lsd_announce_timer; - static void on_announce_disp(boost::weak_ptr p + // used for tracker announces + deadline_timer m_tracker_timer; + + void restart_tracker_timer(ptime announce_at); + + static void on_tracker_announce_disp(boost::weak_ptr p , error_code const& e); - // this is called once per announce interval - void on_announce(); + void on_tracker_announce(); + + static void on_lsd_announce_disp(boost::weak_ptr p + , error_code const& e); + + // this is called once every 5 minutes for torrents + // that are not private + void on_lsd_announce(); #ifndef TORRENT_DISABLE_DHT static void on_dht_announce_response_disp(boost::weak_ptr t @@ -885,9 +895,6 @@ namespace libtorrent // is true if this torrent has been paused bool m_paused:1; - // this is true from the time when the torrent was - // paused to the time should_request() is called - bool m_just_paused:1; // if this is true, libtorrent may pause and resume // this torrent depending on queuing rules. Torrents @@ -937,21 +944,31 @@ namespace libtorrent // before the files are checked, we don't try to // connect to peers bool m_files_checked:1; + + // this is true while tracker announcing is enabled + // is is disabled while paused and checking files + bool m_announcing:1; + + // this is true if event start has been sent to the tracker + bool m_start_sent:1; + + // this is true if event completed has been sent to the tracker + bool m_complete_sent:1; }; inline ptime torrent::next_announce() const { - return m_next_request; + return m_next_tracker_announce; } inline void torrent::force_tracker_request() { - m_next_request = time_now(); + if (!is_paused()) announce_with_tracker(); } inline void torrent::force_tracker_request(ptime t) { - m_next_request = t; + if (!is_paused()) restart_tracker_timer(t); } inline void torrent::set_tracker_login( diff --git a/libtorrent/include/libtorrent/torrent_handle.hpp b/libtorrent/include/libtorrent/torrent_handle.hpp index 25e9bbf21..a062580c2 100644 --- a/libtorrent/include/libtorrent/torrent_handle.hpp +++ b/libtorrent/include/libtorrent/torrent_handle.hpp @@ -322,7 +322,10 @@ namespace libtorrent // fills the specified vector with the download progress [0, 1] // of each file in the torrent. The files are ordered as in // the torrent_info. - void file_progress(std::vector& progress); + void file_progress(std::vector& progress) const TORRENT_DEPRECATED; + void file_progress(std::vector& progress) const; + + void clear_error() const; std::vector const& trackers() const; void replace_trackers(std::vector const&) const; diff --git a/libtorrent/src/disk_io_thread.cpp b/libtorrent/src/disk_io_thread.cpp index adebdcf13..b1b49880f 100644 --- a/libtorrent/src/disk_io_thread.cpp +++ b/libtorrent/src/disk_io_thread.cpp @@ -101,6 +101,22 @@ namespace libtorrent cached_piece_info info; info.piece = i->piece; info.last_use = i->last_use; + info.kind = cached_piece_info::write_cache; + int blocks_in_piece = (ti.piece_size(i->piece) + (m_block_size) - 1) / m_block_size; + info.blocks.resize(blocks_in_piece); + for (int b = 0; b < blocks_in_piece; ++b) + if (i->blocks[b]) info.blocks[b] = true; + ret.push_back(info); + } + for (cache_t::const_iterator i = m_read_pieces.begin() + , end(m_read_pieces.end()); i != end; ++i) + { + torrent_info const& ti = *i->storage->info(); + if (ti.info_hash() != ih) continue; + cached_piece_info info; + info.piece = i->piece; + info.last_use = i->last_use; + info.kind = cached_piece_info::read_cache; int blocks_in_piece = (ti.piece_size(i->piece) + (m_block_size) - 1) / m_block_size; info.blocks.resize(blocks_in_piece); for (int b = 0; b < blocks_in_piece; ++b) diff --git a/libtorrent/src/file.cpp b/libtorrent/src/file.cpp index c6b79dce5..050ec11c9 100755 --- a/libtorrent/src/file.cpp +++ b/libtorrent/src/file.cpp @@ -161,25 +161,19 @@ namespace libtorrent bool open(fs::path const& path, int mode) { close(); -#if defined(_WIN32) && defined(UNICODE) +#if defined _WIN32 && defined UNICODE std::wstring wpath(safe_convert(path.native_file_string())); m_fd = ::_wopen( wpath.c_str() - , map_open_mode(mode) - , S_IREAD | S_IWRITE); -#else -#ifdef _WIN32 + , map_open_mode(mode)); +#elif defined _WIN32 m_fd = ::_open( + utf8_native(path.native_file_string()).c_str() + , map_open_mode(mode)); #else m_fd = ::open( -#endif utf8_native(path.native_file_string()).c_str() - , map_open_mode(mode) -#ifdef _WIN32 - , S_IREAD | S_IWRITE); -#else - , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -#endif + , map_open_mode(mode)); #endif if (m_fd == -1) { diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index fce7f1999..845d014b4 100755 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -632,7 +632,9 @@ namespace libtorrent if (peer_info_struct()) { - peer_info_struct()->on_parole = true; + if (m_ses.settings().use_parole_mode) + peer_info_struct()->on_parole = true; + ++peer_info_struct()->hashfails; boost::int8_t& trust_points = peer_info_struct()->trust_points; @@ -1509,7 +1511,7 @@ namespace libtorrent // just ignore it if (t->is_seed()) { - t->received_redundant_data(p.length); + t->add_redundant_bytes(p.length); return; } @@ -1541,7 +1543,7 @@ namespace libtorrent (*m_logger) << " *** The block we just got was not in the " "request queue ***\n"; #endif - t->received_redundant_data(p.length); + t->add_redundant_bytes(p.length); request_a_block(*t, *this); send_block_requests(); return; @@ -1578,7 +1580,7 @@ namespace libtorrent // if the block we got is already finished, then ignore it if (picker.is_downloaded(block_finished)) { - t->received_redundant_data(p.length); + t->add_redundant_bytes(p.length); m_download_queue.erase(b); m_timeout_extend = 0; @@ -2813,6 +2815,11 @@ namespace libtorrent } m_desired_queue_size = 1; + if (on_parole()) + { + m_timeout_extend += m_ses.settings().request_timeout; + return; + } if (!t->has_picker()) return; piece_picker& picker = t->picker(); @@ -3579,9 +3586,6 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); if (m_disconnecting) { - for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() - , end(m_ses.m_torrents.end()); i != end; ++i) - TORRENT_ASSERT(!i->second->has_peer((peer_connection*)this)); TORRENT_ASSERT(!t); } else if (!m_in_constructor) @@ -3622,11 +3626,13 @@ namespace libtorrent if (!t) { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS // since this connection doesn't have a torrent reference // no torrent should have a reference to this connection either for (aux::session_impl::torrent_map::const_iterator i = m_ses.m_torrents.begin() , end(m_ses.m_torrents.end()); i != end; ++i) TORRENT_ASSERT(!i->second->has_peer((peer_connection*)this)); +#endif return; } @@ -3636,7 +3642,9 @@ namespace libtorrent for (torrent::const_peer_iterator i = t->begin(); i != t->end(); ++i) { // make sure this peer is not a dangling pointer +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS TORRENT_ASSERT(m_ses.has_peer(*i)); +#endif peer_connection const& p = *(*i); for (std::deque::const_iterator i = p.request_queue().begin() , end(p.request_queue().end()); i != end; ++i) @@ -3652,16 +3660,18 @@ namespace libtorrent TORRENT_ASSERT(t->picker().num_peers(i->first) == i->second); } } +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS if (m_peer_info) { policy::const_iterator i; - for (i = t->get_policy().begin_peer(); - i != t->get_policy().end_peer(); ++i) + for (i = t->get_policy().begin_peer() + , end(t->get_policy().end_peer()); i != end; ++i) { if (&i->second == m_peer_info) break; } TORRENT_ASSERT(i != t->get_policy().end_peer()); } +#endif if (t->has_picker() && !t->is_aborted()) { // make sure that pieces that have completed the download diff --git a/libtorrent/src/policy.cpp b/libtorrent/src/policy.cpp index 2567e2424..33845b860 100755 --- a/libtorrent/src/policy.cpp +++ b/libtorrent/src/policy.cpp @@ -502,8 +502,6 @@ namespace libtorrent { INVARIANT_CHECK; - if (m_torrent->is_paused()) return; - // ------------------------ // upload shift // ------------------------ diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index c2a87c73d..774159b66 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -185,6 +185,8 @@ namespace aux { , m_asnum_db(0) , m_country_db(0) #endif + , m_total_failed_bytes(0) + , m_total_redundant_bytes(0) { m_tcp_mapping[0] = -1; m_tcp_mapping[1] = -1; @@ -466,31 +468,9 @@ namespace aux { i != m_torrents.end(); ++i) { torrent& t = *i->second; - - if ((!t.is_paused() || t.should_request()) - && !t.trackers().empty()) - { -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - ++counter; -#endif - tracker_request req = t.generate_tracker_request(); - TORRENT_ASSERT(req.event == tracker_request::stopped); - req.listen_port = 0; - if (!m_listen_sockets.empty()) - req.listen_port = m_listen_sockets.front().external_port; - req.key = m_key; - std::string login = i->second->tracker_login(); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - boost::shared_ptr tl(new tracker_logger(*this)); - m_tracker_loggers.push_back(tl); - m_tracker_manager.queue_request(m_io_service, m_half_open, req, login - , m_listen_interface.address(), tl); -#else - m_tracker_manager.queue_request(m_io_service, m_half_open, req, login - , m_listen_interface.address()); -#endif - } + t.abort(); } + #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) (*m_logger) << time_now_string() << " sent " << counter << " tracker stop requests\n"; #endif @@ -1035,7 +1015,7 @@ namespace aux { // ignore connections that already have a torrent, since they // are ticket through the torrents' second_ticket if (!p->associated_torrent().expired()) continue; - if (m_last_tick - p->connected_time() > seconds(m_settings.peer_connect_timeout)) + if (m_last_tick - p->connected_time() > seconds(m_settings.handshake_timeout)) p->disconnect("timeout: incoming connection"); } @@ -1090,23 +1070,6 @@ namespace aux { num_downloads_peers += t.num_peers(); } - if (t.should_request()) - { - tracker_request req = t.generate_tracker_request(); - req.listen_port = 0; - if (!m_listen_sockets.empty()) - req.listen_port = m_listen_sockets.front().external_port; - req.key = m_key; - m_tracker_manager.queue_request(m_io_service, m_half_open, req - , t.tracker_login(), m_listen_interface.address(), i->second); - - if (m_alerts.should_post()) - { - m_alerts.post_alert( - tracker_announce_alert(t.get_handle(), req.url)); - } - } - t.second_tick(m_stat, tick_interval); ++i; } @@ -1838,32 +1801,6 @@ namespace aux { t.delete_files(); t.abort(); - if ((!t.is_paused() || t.should_request()) - && !t.trackers().empty()) - { - tracker_request req = t.generate_tracker_request(); - TORRENT_ASSERT(req.event == tracker_request::stopped); - req.listen_port = 0; - if (!m_listen_sockets.empty()) - req.listen_port = m_listen_sockets.front().external_port; - req.key = m_key; - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - boost::shared_ptr tl(new tracker_logger(*this)); - m_tracker_loggers.push_back(tl); - m_tracker_manager.queue_request(m_io_service, m_half_open, req - , t.tracker_login(), m_listen_interface.address(), tl); -#else - m_tracker_manager.queue_request(m_io_service, m_half_open, req - , t.tracker_login(), m_listen_interface.address()); -#endif - - if (m_alerts.should_post()) - { - m_alerts.post_alert( - tracker_announce_alert(t.get_handle(), req.url)); - } - } #ifndef NDEBUG sha1_hash i_hash = t.torrent_file().info_hash(); #endif @@ -2017,6 +1954,9 @@ namespace aux { s.num_unchoked = m_num_unchoked; s.allowed_upload_slots = m_allowed_upload_slots; + s.total_redundant_bytes = m_total_redundant_bytes; + s.total_failed_bytes = m_total_failed_bytes; + s.up_bandwidth_queue = m_upload_channel.queue_size(); s.down_bandwidth_queue = m_download_channel.queue_size(); diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index afd792984..dd388de2b 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -155,11 +155,11 @@ namespace libtorrent , m_started(time_now()) , m_last_scrape(min_time()) , m_torrent_file(tf) - , m_event(tracker_request::started) , m_storage(0) - , m_next_request(time_now()) + , m_next_tracker_announce(time_now()) , m_host_resolver(ses.m_io_service) - , m_announce_timer(ses.m_io_service) + , m_lsd_announce_timer(ses.m_io_service) + , m_tracker_timer(ses.m_io_service) #ifndef TORRENT_DISABLE_DHT , m_last_dht_announce(time_now() - minutes(15)) #endif @@ -191,7 +191,6 @@ namespace libtorrent , m_time_scaler(0) , m_abort(false) , m_paused(paused) - , m_just_paused(false) , m_auto_managed(auto_managed) #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES , m_resolving_country(false) @@ -202,6 +201,9 @@ namespace libtorrent , m_connections_initialized(true) , m_has_incoming(false) , m_files_checked(false) + , m_announcing(false) + , m_start_sent(false) + , m_complete_sent(false) { parse_resume_data(resume_data); } @@ -228,11 +230,11 @@ namespace libtorrent , m_started(time_now()) , m_last_scrape(min_time()) , m_torrent_file(new torrent_info(info_hash)) - , m_event(tracker_request::started) , m_storage(0) - , m_next_request(time_now()) + , m_next_tracker_announce(time_now()) , m_host_resolver(ses.m_io_service) - , m_announce_timer(ses.m_io_service) + , m_lsd_announce_timer(ses.m_io_service) + , m_tracker_timer(ses.m_io_service) #ifndef TORRENT_DISABLE_DHT , m_last_dht_announce(time_now() - minutes(15)) #endif @@ -263,7 +265,6 @@ namespace libtorrent , m_time_scaler(0) , m_abort(false) , m_paused(paused) - , m_just_paused(false) , m_auto_managed(auto_managed) #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES , m_resolving_country(false) @@ -273,6 +274,10 @@ namespace libtorrent , m_got_tracker_response(false) , m_connections_initialized(false) , m_has_incoming(false) + , m_files_checked(false) + , m_announcing(false) + , m_start_sent(false) + , m_complete_sent(false) { parse_resume_data(resume_data); #ifndef NDEBUG @@ -310,13 +315,8 @@ namespace libtorrent void torrent::start() { - boost::weak_ptr self(shared_from_this()); if (m_torrent_file->is_valid()) init(); if (m_abort) return; - error_code ec; - m_announce_timer.expires_from_now(seconds(1), ec); - m_announce_timer.async_wait( - bind(&torrent::on_announce_disp, self, _1)); } #ifndef TORRENT_DISABLE_DHT @@ -748,41 +748,57 @@ namespace libtorrent m_net_interface = tcp::endpoint(address::from_string(net_interface), 0); } - void torrent::on_announce_disp(boost::weak_ptr p + void torrent::on_tracker_announce_disp(boost::weak_ptr p , error_code const& e) { if (e) return; boost::shared_ptr t = p.lock(); if (!t) return; - t->on_announce(); + t->on_tracker_announce(); } - void torrent::on_announce() + void torrent::on_tracker_announce() { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + if (m_abort) return; + announce_with_tracker(); + } + + void torrent::on_lsd_announce_disp(boost::weak_ptr p + , error_code const& e) + { + if (e) return; + boost::shared_ptr t = p.lock(); + if (!t) return; + t->on_lsd_announce(); + } + + void torrent::on_lsd_announce() + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + if (m_abort) return; + + TORRENT_ASSERT(!m_torrent_file->priv()); + if (m_torrent_file->is_valid() && m_torrent_file->priv()) + return; + + if (is_paused()) return; boost::weak_ptr self(shared_from_this()); error_code ec; - if (!m_torrent_file->priv()) - { - // announce on local network every 5 minutes - m_announce_timer.expires_from_now(minutes(5), ec); - m_announce_timer.async_wait( - bind(&torrent::on_announce_disp, self, _1)); - // announce with the local discovery service - if (!is_paused()) m_ses.announce_lsd(m_torrent_file->info_hash()); - } - else - { - m_announce_timer.expires_from_now(minutes(15), ec); - m_announce_timer.async_wait( - bind(&torrent::on_announce_disp, self, _1)); - } + // announce on local network every 5 minutes + m_lsd_announce_timer.expires_from_now(minutes(5), ec); + m_lsd_announce_timer.async_wait( + bind(&torrent::on_lsd_announce_disp, self, _1)); + + // announce with the local discovery service + m_ses.announce_lsd(m_torrent_file->info_hash()); #ifndef TORRENT_DISABLE_DHT - if (is_paused()) return; if (!m_ses.m_dht) return; ptime now = time_now(); if (should_announce_dht() && now - m_last_dht_announce > minutes(14)) @@ -821,6 +837,61 @@ namespace libtorrent #endif + void torrent::announce_with_tracker(tracker_request::event_t e) + { + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_trackers.empty()); + + restart_tracker_timer(time_now() + seconds(tracker_retry_delay_max)); + + if (e == tracker_request::none) + { + if (!m_start_sent) e = tracker_request::started; + if (!m_complete_sent && is_seed()) e = tracker_request::completed; + } + + tracker_request req; + req.info_hash = m_torrent_file->info_hash(); + req.pid = m_ses.get_peer_id(); + req.downloaded = m_stat.total_payload_download(); + req.uploaded = m_stat.total_payload_upload(); + req.left = bytes_left(); + if (req.left == -1) req.left = 16*1024; + req.event = e; + tcp::endpoint ep = m_ses.get_ipv6_interface(); + if (ep != tcp::endpoint()) + req.ipv6 = ep.address().to_string(); + + req.url = m_trackers[m_currently_trying_tracker].url; + // if we are aborting. we don't want any new peers + req.num_want = (req.event == tracker_request::stopped) + ?0:m_settings.num_want; + + req.listen_port = m_ses.m_listen_sockets.empty() + ?0:m_ses.m_listen_sockets.front().external_port; + req.key = m_ses.m_key; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + if (m_abort) + { + boost::shared_ptr tl(new tracker_logger(m_ses)); + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login(), m_ses.m_listen_interface.address(), tl); + } + else +#endif + m_ses.m_tracker_manager.queue_request(m_ses.m_io_service, m_ses.m_half_open, req + , tracker_login(), m_ses.m_listen_interface.address() + , m_abort?boost::shared_ptr():shared_from_this()); + + if (m_ses.m_alerts.should_post()) + { + m_ses.m_alerts.post_alert( + tracker_announce_alert(get_handle(), req.url, req.event)); + } + } + void torrent::scrape_tracker() { if (m_trackers.empty()) return; @@ -838,23 +909,6 @@ namespace libtorrent m_last_scrape = time_now(); } - // returns true if it is time for this torrent to make another - // tracker request - bool torrent::should_request() - { -// INVARIANT_CHECK; - - if (m_trackers.empty()) return false; - if (!m_files_checked) return false; - - if (m_just_paused) - { - m_just_paused = false; - return true; - } - return !is_paused() && m_next_request < time_now(); - } - void torrent::tracker_warning(tracker_request const& req, std::string const& msg) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); @@ -899,6 +953,11 @@ namespace libtorrent if (external_ip != address()) m_ses.set_external_address(external_ip); + if (!m_start_sent && r.event == tracker_request::started) + m_start_sent = true; + if (!m_complete_sent && r.event == tracker_request::completed) + m_complete_sent = true; + m_failed_trackers = 0; // announce intervals less than 5 minutes // are insane. @@ -909,16 +968,13 @@ namespace libtorrent m_currently_trying_tracker = 0; m_duration = interval; - m_next_request = time_now() + seconds(m_duration); + restart_tracker_timer(time_now() + seconds(m_duration)); if (complete >= 0) m_complete = complete; if (incomplete >= 0) m_incomplete = incomplete; if (complete >= 0 && incomplete >= 0) m_last_scrape = time_now(); - // connect to random peers from the list - std::random_shuffle(peer_list.begin(), peer_list.end()); - #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING std::stringstream s; s << "TRACKER RESPONSE:\n" @@ -1363,7 +1419,7 @@ namespace libtorrent m_ses.m_alerts.post_alert(hash_failed_alert(get_handle(), index)); // increase the total amount of failed bytes - m_total_failed_bytes += m_torrent_file->piece_size(index); + add_failed_bytes(m_torrent_file->piece_size(index)); std::vector downloaders; m_picker->get_downloaders(downloaders, index); @@ -1503,9 +1559,9 @@ namespace libtorrent // if the torrent is paused, it doesn't need // to announce with even=stopped again. if (!is_paused()) - m_event = tracker_request::stopped; - // disconnect all peers and close all - // files belonging to the torrents + { + stop_announcing(); + } #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING for (peer_iterator i = m_connections.begin(); @@ -1515,14 +1571,14 @@ namespace libtorrent } #endif + // disconnect all peers and close all + // files belonging to the torrents disconnect_all(); if (m_owning_storage.get()) m_storage->async_release_files( bind(&torrent::on_files_released, shared_from_this(), _1, _2)); m_owning_storage = 0; - error_code ec; - m_announce_timer.cancel(ec); m_host_resolver.cancel(); } @@ -1880,42 +1936,6 @@ namespace libtorrent m_last_working_tracker = -1; } - tracker_request torrent::generate_tracker_request() - { - INVARIANT_CHECK; - - TORRENT_ASSERT(!m_trackers.empty()); - - m_next_request = time_now() + seconds(tracker_retry_delay_max); - - tracker_request req; - req.info_hash = m_torrent_file->info_hash(); - req.pid = m_ses.get_peer_id(); - req.downloaded = m_stat.total_payload_download(); - req.uploaded = m_stat.total_payload_upload(); - req.left = bytes_left(); - if (req.left == -1) req.left = 16*1024; - req.event = m_event; - tcp::endpoint ep = m_ses.get_ipv6_interface(); - if (ep != tcp::endpoint()) - req.ipv6 = ep.address().to_string(); - - if (m_event != tracker_request::stopped) - m_event = tracker_request::none; - req.url = m_trackers[m_currently_trying_tracker].url; - req.num_want = m_settings.num_want; - // if we are aborting. we don't want any new peers - if (req.event == tracker_request::stopped) - req.num_want = 0; - - // default initialize, these should be set by caller - // before passing the request to the tracker_manager - req.listen_port = 0; - req.key = 0; - - return req; - } - void torrent::choke_peer(peer_connection& c) { INVARIANT_CHECK; @@ -3094,11 +3114,8 @@ namespace libtorrent m_picker.reset(); - // make the next tracker request - // be a completed-event - m_event = tracker_request::completed; set_state(torrent_status::seeding); - force_tracker_request(); + if (!m_complete_sent && m_announcing) announce_with_tracker(); } // this will move the tracker with the given index @@ -3119,46 +3136,48 @@ namespace libtorrent return index; } - void torrent::try_next_tracker() + void torrent::try_next_tracker(tracker_request const& req) { INVARIANT_CHECK; ++m_currently_trying_tracker; - if ((unsigned)m_currently_trying_tracker >= m_trackers.size()) + if ((unsigned)m_currently_trying_tracker < m_trackers.size()) { - int delay = tracker_retry_delay_min - + (std::min)(int(m_failed_trackers), int(tracker_failed_max)) - * (tracker_retry_delay_max - tracker_retry_delay_min) - / tracker_failed_max; + announce_with_tracker(req.event); + return; + } - ++m_failed_trackers; - // if we've looped the tracker list, wait a bit before retrying - m_currently_trying_tracker = 0; - m_next_request = time_now() + seconds(delay); + int delay = tracker_retry_delay_min + + (std::min)(int(m_failed_trackers), int(tracker_failed_max)) + * (tracker_retry_delay_max - tracker_retry_delay_min) + / tracker_failed_max; + + ++m_failed_trackers; + // if we've looped the tracker list, wait a bit before retrying + m_currently_trying_tracker = 0; + + // if we're stopping, just give up. Don't bother retrying + if (req.event == tracker_request::stopped) + return; + + restart_tracker_timer(time_now() + seconds(delay)); #ifndef TORRENT_DISABLE_DHT - if (m_abort) return; + if (m_abort) return; - // only start the announce if we want to announce with the dht - ptime now = time_now(); - if (should_announce_dht() && now - m_last_dht_announce > minutes(14)) - { - // force the DHT to reannounce - m_last_dht_announce = now; - boost::weak_ptr self(shared_from_this()); - m_ses.m_dht->announce(m_torrent_file->info_hash() - , m_ses.m_listen_sockets.front().external_port - , bind(&torrent::on_dht_announce_response_disp, self, _1)); - } -#endif - - } - else + // only start the announce if we want to announce with the dht + ptime now = time_now(); + if (should_announce_dht() && now - m_last_dht_announce > minutes(14)) { - // don't delay before trying the next tracker - m_next_request = time_now(); + // force the DHT to reannounce + m_last_dht_announce = now; + boost::weak_ptr self(shared_from_this()); + m_ses.m_dht->announce(m_torrent_file->info_hash() + , m_ses.m_listen_sockets.front().external_port + , bind(&torrent::on_dht_announce_response_disp, self, _1)); } +#endif } @@ -3196,7 +3215,11 @@ namespace libtorrent } #endif - if (is_seed()) finished(); + if (is_seed()) + { + m_complete_sent = true; + finished(); + } if (!m_connections_initialized) { @@ -3231,6 +3254,8 @@ namespace libtorrent } m_files_checked = true; + + start_announcing(); } alert_manager& torrent::alerts() const @@ -3326,8 +3351,10 @@ namespace libtorrent std::map num_requests; for (const_peer_iterator i = begin(); i != end(); ++i) { +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS // make sure this peer is not a dangling pointer TORRENT_ASSERT(m_ses.has_peer(*i)); +#endif peer_connection const& p = *(*i); for (std::deque::const_iterator i = p.request_queue().begin() , end(p.request_queue().end()); i != end; ++i) @@ -3362,11 +3389,13 @@ namespace libtorrent TORRENT_ASSERT(m_abort || m_picker->num_pieces() == 0); } +#ifdef TORRENT_EXPENSIVE_INVARIANT_CHECKS for (policy::const_iterator i = m_policy.begin_peer() , end(m_policy.end_peer()); i != end; ++i) { TORRENT_ASSERT(i->second.ip.address() == i->first); } +#endif size_type total_done = quantized_bytes_done(); if (m_torrent_file->is_valid()) @@ -3569,11 +3598,7 @@ namespace libtorrent #endif disconnect_all(); - if (!is_paused()) - m_just_paused = true; - m_paused = true; - // tell the tracker that we stopped - m_event = tracker_request::stopped; + stop_announcing(); if (m_owning_storage.get()) { @@ -3583,6 +3608,14 @@ namespace libtorrent } } + void torrent::clear_error() + { + if (m_error.empty()) return; + if (m_ses.m_auto_manage_time_scaler > 2) + m_ses.m_auto_manage_time_scaler = 2; + m_error.clear(); + } + void torrent::auto_managed(bool a) { INVARIANT_CHECK; @@ -3725,10 +3758,6 @@ namespace libtorrent } #endif - disconnect_all(); - // tell the tracker that we stopped - m_event = tracker_request::stopped; - m_just_paused = true; // this will make the storage close all // files and flush all cached data if (m_owning_storage.get()) @@ -3742,6 +3771,9 @@ namespace libtorrent if (alerts().should_post()) alerts().post_alert(torrent_paused_alert(get_handle())); } + + disconnect_all(); + stop_announcing(); } void torrent::resume() @@ -3771,18 +3803,64 @@ namespace libtorrent } #endif - m_started = time_now(); - m_error.clear(); - if (alerts().should_post()) alerts().post_alert(torrent_resumed_alert(get_handle())); - // tell the tracker that we're back - m_event = tracker_request::started; - force_tracker_request(); + m_started = time_now(); + m_error.clear(); + start_announcing(); + } - // make pulse be called as soon as possible - m_time_scaler = 0; + void torrent::restart_tracker_timer(ptime announce_at) + { + if (!m_announcing) return; + + m_next_tracker_announce = announce_at; + error_code ec; + boost::weak_ptr self(shared_from_this()); + m_tracker_timer.expires_at(m_next_tracker_announce, ec); + m_tracker_timer.async_wait(bind(&torrent::on_tracker_announce_disp, self, _1)); + } + + void torrent::start_announcing() + { + if (is_paused()) return; + if (!m_files_checked) return; + if (m_announcing) return; + + m_announcing = true; + + if (!m_trackers.empty()) + { + // tell the tracker that we're back + m_start_sent = false; + announce_with_tracker(); + } + + // private torrents are never announced on LSD + // or on DHT, we don't need this timer. + if (!m_torrent_file->is_valid() || !m_torrent_file->priv()) + { + error_code ec; + boost::weak_ptr self(shared_from_this()); + m_lsd_announce_timer.expires_from_now(seconds(1), ec); + m_lsd_announce_timer.async_wait( + bind(&torrent::on_lsd_announce_disp, self, _1)); + } + } + + void torrent::stop_announcing() + { + if (!m_announcing) return; + + error_code ec; + m_lsd_announce_timer.cancel(ec); + m_tracker_timer.cancel(ec); + + m_announcing = false; + + if (!m_trackers.empty()) + announce_with_tracker(tracker_request::stopped); } void torrent::second_tick(stat& accumulator, float tick_interval) @@ -3803,7 +3881,7 @@ namespace libtorrent } #endif - if (m_paused) + if (is_paused()) { // let the stats fade out to 0 m_stat.second_tick(tick_interval); @@ -3816,7 +3894,7 @@ namespace libtorrent // ---- WEB SEEDS ---- - // re-insert urls that are to be retries into the m_web_seeds + // re-insert urls that are to be retrieds into the m_web_seeds typedef std::map::iterator iter_t; for (iter_t i = m_web_seeds_next_retry.begin(); i != m_web_seeds_next_retry.end();) { @@ -3972,18 +4050,34 @@ namespace libtorrent } void torrent::file_progress(std::vector& fp) const + { + fp.clear(); + fp.resize(m_torrent_file->num_files(), 1.f); + if (is_seed()) return; + + std::vector progress; + file_progress(progress); + for (int i = 0; i < m_torrent_file->num_files(); ++i) + { + file_entry const& f = m_torrent_file->file_at(i); + if (f.size == 0) fp[i] = 1.f; + else fp[i] = float(progress[i]) / f.size; + } + } + + void torrent::file_progress(std::vector& fp) const { TORRENT_ASSERT(valid_metadata()); - fp.clear(); + fp.resize(m_torrent_file->num_files(), 0); + TORRENT_ASSERT(has_picker()); + if (is_seed()) { - fp.resize(m_torrent_file->num_files(), 1.f); + for (int i = 0; i < m_torrent_file->num_files(); ++i) + fp[i] = m_torrent_file->files().at(i).size; return; } - - TORRENT_ASSERT(has_picker()); - fp.resize(m_torrent_file->num_files(), 0.f); for (int i = 0; i < m_torrent_file->num_files(); ++i) { @@ -3994,7 +4088,7 @@ namespace libtorrent // 100% done all the time if (size == 0) { - fp[i] = 1.f; + fp[i] = 0; continue; } @@ -4010,7 +4104,7 @@ namespace libtorrent } TORRENT_ASSERT(size == 0); - fp[i] = static_cast(done) / m_torrent_file->files().at(i).size; + fp[i] = done; } const std::vector& q @@ -4026,28 +4120,56 @@ namespace libtorrent piece_picker::block_info const* info = i->info; for (int k = 0; k < num_blocks; ++k) { - if (info[k].state != piece_picker::block_info::state_writing - && info[k].state != piece_picker::block_info::state_finished) + TORRENT_ASSERT(offset == size_type(i->index) * m_torrent_file->piece_length() + + k * m_block_size); + + size_type block_size = m_block_size; + + if (info[k].state == piece_picker::block_info::state_none) + { + offset += m_block_size; continue; + } + + if (info[k].state == piece_picker::block_info::state_requested) + { + block_size = 0; + policy::peer* p = static_cast(info[k].peer); + if (p && p->connection) + { + boost::optional pbp + = p->connection->downloading_piece_progress(); + if (pbp && pbp->piece_index == i->index && pbp->block_index == k) + block_size = pbp->bytes_downloaded; + } + + if (block_size == 0) + { + offset += m_block_size; + continue; + } + } + if (offset + m_block_size > file->offset + file->size) { + int left_over = m_block_size - block_size; // split the block on multiple files - size_type block_size = m_block_size; while (offset + block_size > file->offset + file->size) { TORRENT_ASSERT(offset <= file->offset + file->size); size_type slice = file->offset + file->size - offset; - fp[file_index] += float(slice) / file->size; + fp[file_index] += slice; offset += slice; block_size -= slice; ++file; ++file_index; if (file == m_torrent_file->end_files()) break; } + offset += left_over; } else { - fp[file_index] += float(m_block_size) / file->size; + fp[file_index] += block_size; offset += m_block_size; } } @@ -4208,6 +4330,20 @@ namespace libtorrent return st; } + void torrent::add_redundant_bytes(int b) + { + TORRENT_ASSERT(b > 0); + m_total_redundant_bytes += b; + m_ses.add_redundant_bytes(b); + } + + void torrent::add_failed_bytes(int b) + { + TORRENT_ASSERT(b > 0); + m_total_failed_bytes += b; + m_ses.add_failed_bytes(b); + } + int torrent::num_seeds() const { INVARIANT_CHECK; @@ -4245,7 +4381,7 @@ namespace libtorrent } if (r.kind == tracker_request::announce_request) - try_next_tracker(); + try_next_tracker(r); } // TODO: with some response codes, we should just consider @@ -4278,7 +4414,7 @@ namespace libtorrent } if (r.kind == tracker_request::announce_request) - try_next_tracker(); + try_next_tracker(r); } diff --git a/libtorrent/src/torrent_handle.cpp b/libtorrent/src/torrent_handle.cpp index 31e28e3a6..4b8d8a452 100755 --- a/libtorrent/src/torrent_handle.cpp +++ b/libtorrent/src/torrent_handle.cpp @@ -319,6 +319,12 @@ namespace libtorrent TORRENT_FORWARD(set_queue_position((std::numeric_limits::max)())); } + void torrent_handle::clear_error() const + { + INVARIANT_CHECK; + TORRENT_FORWARD(clear_error()); + } + void torrent_handle::set_tracker_login(std::string const& name , std::string const& password) const { @@ -326,7 +332,13 @@ namespace libtorrent TORRENT_FORWARD(set_tracker_login(name, password)); } - void torrent_handle::file_progress(std::vector& progress) + void torrent_handle::file_progress(std::vector& progress) const + { + INVARIANT_CHECK; + TORRENT_FORWARD(file_progress(progress)); + } + + void torrent_handle::file_progress(std::vector& progress) const { INVARIANT_CHECK; TORRENT_FORWARD(file_progress(progress));