diff --git a/libtorrent/include/libtorrent/alert_types.hpp b/libtorrent/include/libtorrent/alert_types.hpp index 1dc843ede..4d82641a6 100755 --- a/libtorrent/include/libtorrent/alert_types.hpp +++ b/libtorrent/include/libtorrent/alert_types.hpp @@ -56,39 +56,55 @@ namespace libtorrent struct TORRENT_EXPORT tracker_alert: torrent_alert { tracker_alert(torrent_handle const& h + , std::string const& url_ + , alert::severity_t s + , std::string const& msg) + : torrent_alert(h, s, msg) + , url(url_) + {} + + std::string url; + }; + + struct TORRENT_EXPORT tracker_error_alert: tracker_alert + { + tracker_error_alert(torrent_handle const& h , int times , int status + , std::string const& url , std::string const& msg) - : torrent_alert(h, alert::warning, msg) + : tracker_alert(h, url, alert::warning, msg) , times_in_row(times) , status_code(status) {} virtual std::auto_ptr clone() const - { return std::auto_ptr(new tracker_alert(*this)); } + { return std::auto_ptr(new tracker_error_alert(*this)); } int times_in_row; int status_code; }; - struct TORRENT_EXPORT tracker_warning_alert: torrent_alert + struct TORRENT_EXPORT tracker_warning_alert: tracker_alert { tracker_warning_alert(torrent_handle const& h + , std::string const& url , std::string const& msg) - : torrent_alert(h, alert::warning, msg) + : tracker_alert(h, url, alert::warning, msg) {} virtual std::auto_ptr clone() const { return std::auto_ptr(new tracker_warning_alert(*this)); } }; - struct TORRENT_EXPORT scrape_reply_alert: torrent_alert + struct TORRENT_EXPORT scrape_reply_alert: tracker_alert { scrape_reply_alert(torrent_handle const& h , int incomplete_ , int complete_ + , std::string const& url , std::string const& msg) - : torrent_alert(h, alert::info, msg) + : tracker_alert(h, url, alert::info, msg) , incomplete(incomplete_) , complete(complete_) {} @@ -100,23 +116,25 @@ namespace libtorrent { return std::auto_ptr(new scrape_reply_alert(*this)); } }; - struct TORRENT_EXPORT scrape_failed_alert: torrent_alert + struct TORRENT_EXPORT scrape_failed_alert: tracker_alert { scrape_failed_alert(torrent_handle const& h + , std::string const& url , std::string const& msg) - : torrent_alert(h, alert::warning, msg) + : tracker_alert(h, url, alert::warning, msg) {} virtual std::auto_ptr clone() const { return std::auto_ptr(new scrape_failed_alert(*this)); } }; - struct TORRENT_EXPORT tracker_reply_alert: torrent_alert + struct TORRENT_EXPORT tracker_reply_alert: tracker_alert { tracker_reply_alert(torrent_handle const& h , int np + , std::string const& url , std::string const& msg) - : torrent_alert(h, alert::info, msg) + : tracker_alert(h, url, alert::info, msg) , num_peers(np) {} @@ -126,10 +144,12 @@ namespace libtorrent { return std::auto_ptr(new tracker_reply_alert(*this)); } }; - struct TORRENT_EXPORT tracker_announce_alert: torrent_alert + struct TORRENT_EXPORT tracker_announce_alert: tracker_alert { - tracker_announce_alert(torrent_handle const& h, std::string const& msg) - : torrent_alert(h, alert::info, msg) + tracker_announce_alert(torrent_handle const& h + , std::string const& url + , std::string const& msg) + : tracker_alert(h, url, alert::info, msg) {} virtual std::auto_ptr clone() const diff --git a/libtorrent/include/libtorrent/aux_/session_impl.hpp b/libtorrent/include/libtorrent/aux_/session_impl.hpp index dd7f882c4..47016f808 100644 --- a/libtorrent/include/libtorrent/aux_/session_impl.hpp +++ b/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -198,25 +198,7 @@ namespace libtorrent , const char* net_interface = 0); bool is_listening() const; - torrent_handle add_torrent( - boost::intrusive_ptr ti - , fs::path const& save_path - , entry const& resume_data - , storage_mode_t storage_mode - , storage_constructor_type sc - , bool paused - , void* userdata); - - torrent_handle add_torrent( - char const* tracker_url - , sha1_hash const& info_hash - , char const* name - , fs::path const& save_path - , entry const& resume_data - , storage_mode_t storage_mode - , storage_constructor_type sc - , bool paused - , void* userdata); + torrent_handle add_torrent(add_torrent_params const&); void remove_torrent(torrent_handle const& h, int options); @@ -491,6 +473,10 @@ namespace libtorrent // recomputed. int m_unchoke_time_scaler; + // this is used to decide when to recalculate which + // torrents to keep queued and which to activate + int m_auto_manage_time_scaler; + // works like unchoke_time_scaler but it // is only decresed when the unchoke set // is recomputed, and when it reaches zero, @@ -513,6 +499,10 @@ namespace libtorrent bool m_incoming_connection; void second_tick(asio::error_code const& e); + void recalculate_auto_managed_torrents(); + void recalculate_unchoke_slots(int congested_torrents + , int uncongested_torrents); + ptime m_last_tick; // when outgoing_ports is configured, this is the @@ -532,6 +522,10 @@ namespace libtorrent // but for the udp port used by the DHT. int m_external_udp_port; + // the sequence number to assign to the + // next torrent that's added + int m_torrent_sequence; + udp_socket m_dht_socket; void on_receive_udp(asio::error_code const& e @@ -613,7 +607,8 @@ namespace libtorrent struct tracker_logger : request_callback { tracker_logger(session_impl& ses): m_ses(ses) {} - void tracker_warning(std::string const& str) + void tracker_warning(tracker_request const& req + , std::string const& str) { debug_log("*** tracker warning: " + str); } diff --git a/libtorrent/include/libtorrent/policy.hpp b/libtorrent/include/libtorrent/policy.hpp index 29753a11e..e5e76f9ab 100755 --- a/libtorrent/include/libtorrent/policy.hpp +++ b/libtorrent/include/libtorrent/policy.hpp @@ -260,8 +260,17 @@ namespace libtorrent m_num_connect_candidates = 1; } + void erase_peer(iterator i); + private: + bool compare_peer(policy::peer const& lhs, policy::peer const& rhs + , address const& external_ip) const; + + // since the peer list can grow too large + // to scan all of it, start at this iterator + iterator m_round_robin; + iterator find_disconnect_candidate(); iterator find_connect_candidate(); diff --git a/libtorrent/include/libtorrent/session.hpp b/libtorrent/include/libtorrent/session.hpp index 45bb27688..fcae0be32 100755 --- a/libtorrent/include/libtorrent/session.hpp +++ b/libtorrent/include/libtorrent/session.hpp @@ -62,6 +62,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/fingerprint.hpp" #include "libtorrent/time.hpp" #include "libtorrent/disk_io_thread.hpp" +#include "libtorrent/peer_id.hpp" #include "libtorrent/storage.hpp" @@ -120,6 +121,34 @@ namespace libtorrent boost::shared_ptr m_impl; }; + struct add_torrent_params + { + add_torrent_params(storage_constructor_type sc = default_storage_constructor) + : tracker_url(0) + , name(0) + , resume_data(0) + , storage_mode(storage_mode_sparse) + , paused(true) + , auto_managed(true) + , duplicate_is_error(false) + , storage(sc) + , userdata(0) + {} + + boost::intrusive_ptr ti; + char const* tracker_url; + sha1_hash info_hash; + char const* name; + fs::path save_path; + entry const* resume_data; + storage_mode_t storage_mode; + bool paused; + bool auto_managed; + bool duplicate_is_error; + storage_constructor_type storage; + void* userdata; + }; + class TORRENT_EXPORT session: public boost::noncopyable, aux::eh_initializer { public: @@ -148,6 +177,9 @@ namespace libtorrent torrent_handle find_torrent(sha1_hash const& info_hash) const; // all torrent_handles must be destructed before the session is destructed! + torrent_handle add_torrent(add_torrent_params const& params); + + // deprecated in 0.14 torrent_handle add_torrent( torrent_info const& ti , fs::path const& save_path @@ -156,6 +188,7 @@ namespace libtorrent , bool paused = false , storage_constructor_type sc = default_storage_constructor) TORRENT_DEPRECATED; + // deprecated in 0.14 torrent_handle add_torrent( boost::intrusive_ptr ti , fs::path const& save_path @@ -163,8 +196,9 @@ namespace libtorrent , storage_mode_t storage_mode = storage_mode_sparse , bool paused = false , storage_constructor_type sc = default_storage_constructor - , void* userdata = 0); + , void* userdata = 0) TORRENT_DEPRECATED; + // deprecated in 0.14 torrent_handle add_torrent( char const* tracker_url , sha1_hash const& info_hash @@ -174,7 +208,7 @@ namespace libtorrent , storage_mode_t storage_mode = storage_mode_sparse , bool paused = false , storage_constructor_type sc = default_storage_constructor - , void* userdata = 0); + , void* userdata = 0) TORRENT_DEPRECATED; session_proxy abort() { return session_proxy(m_impl); } diff --git a/libtorrent/include/libtorrent/session_settings.hpp b/libtorrent/include/libtorrent/session_settings.hpp index eb6fa35ca..069eea08d 100644 --- a/libtorrent/include/libtorrent/session_settings.hpp +++ b/libtorrent/include/libtorrent/session_settings.hpp @@ -128,6 +128,12 @@ namespace libtorrent , cache_expiry(60) , outgoing_ports(0,0) , peer_tos(0) + , active_downloads(8) + , active_seeds(5) + , auto_manage_interval(30) + , share_ratio_limit(2.f) + , seed_time_ratio_limit(7.f) + , seed_time_limit(24 * 60 * 60) // 24 hours {} // this is the user agent that will be sent to the tracker @@ -347,8 +353,31 @@ namespace libtorrent // http://qbone.internet2.edu/qbss/ // For unmarked packets, set to 0 char peer_tos; - }; + + // for auto managed torrents, these are the limits + // they are subject to. If there are too many torrents + // some of the auto managed ones will be paused until + // some slots free up. + int active_downloads; + int active_seeds; + + // the number of seconds in between recalculating which + // torrents to activate and which ones to queue + int auto_manage_interval; + // when a seeding torrent reaches eaither the share ratio + // (bytes up / bytes down) or the seed time ratio + // (seconds as seed / seconds as downloader) or the seed + // time limit (seconds as seed) it is considered + // done, and it will leave room for other torrents + // the default value for share ratio is 2 + // the default seed time ratio is 7, because that's a common + // asymmetry ratio on connections + float share_ratio_limit; + float seed_time_ratio_limit; + int seed_time_limit; + }; + #ifndef TORRENT_DISABLE_DHT struct dht_settings { diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index 801d99598..e6f3b685d 100755 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -105,7 +105,9 @@ namespace libtorrent , int block_size , storage_constructor_type sc , bool paused - , entry const& resume_data); + , entry const* resume_data + , int seq + , bool auto_managed); // used with metadata-less torrents // (the metadata is downloaded from the peers) @@ -120,7 +122,9 @@ namespace libtorrent , int block_size , storage_constructor_type sc , bool paused - , entry const& resume_data); + , entry const* resume_data + , int seq + , bool auto_managed); ~torrent(); @@ -147,6 +151,9 @@ namespace libtorrent void files_checked(); void start_checking(); + float seed_cycles(session_settings const& s) const; + int seed_cycles_int(session_settings const& s) const { return int(seed_cycles(s)); } + storage_mode_t storage_mode() const { return m_storage_mode; } // this will flag the torrent as aborted. The main // loop in session_impl will check for this state @@ -180,6 +187,9 @@ namespace libtorrent bool is_paused() const { return m_paused; } void save_resume_data(); + bool is_auto_managed() const { return m_auto_managed; } + void auto_managed(bool a); + void delete_files(); // ============ start deprecation ============= @@ -318,7 +328,8 @@ namespace libtorrent tracker_request const& r); virtual void tracker_request_error(tracker_request const& r , int response_code, const std::string& str); - virtual void tracker_warning(std::string const& msg); + virtual void tracker_warning(tracker_request const& req + , std::string const& msg); virtual void tracker_scrape_response(tracker_request const& req , int complete, int incomplete, int downloaded); @@ -561,6 +572,8 @@ namespace libtorrent // a return value of false indicates an error bool set_metadata(entry const& metadata, std::string& error); + int sequence_number() const { return m_sequence_number; } + private: void on_files_deleted(int ret, disk_io_job const& j); @@ -592,6 +605,13 @@ namespace libtorrent // paused to the time should_request() is called bool m_just_paused; + // if this is true, libtorrent may pause and resume + // this torrent depending on queuing rules. Torrents + // started with auto_managed flag set may be added in + // a paused state in case there are no available + // slots. + bool m_auto_managed; + tracker_request::event_t m_event; void parse_response(const entry& e, std::vector& peer_list); diff --git a/libtorrent/include/libtorrent/torrent_handle.hpp b/libtorrent/include/libtorrent/torrent_handle.hpp index 4d0a1b3ac..d370770bc 100755 --- a/libtorrent/include/libtorrent/torrent_handle.hpp +++ b/libtorrent/include/libtorrent/torrent_handle.hpp @@ -118,6 +118,7 @@ namespace libtorrent , all_time_download(0) , active_time(0) , seeding_time(0) + , seed_cycles(0.f) {} enum state_t @@ -255,6 +256,8 @@ namespace libtorrent // from resume data int active_time; int seeding_time; + + float seed_cycles; }; struct TORRENT_EXPORT block_info @@ -332,6 +335,9 @@ namespace libtorrent void resume() const; void save_resume_data() const; + bool is_auto_managed() const; + void auto_managed(bool m) const; + #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES void resolve_countries(bool r); bool resolve_countries() const; @@ -342,6 +348,7 @@ namespace libtorrent // ================ start deprecation ============ + // deprecated in 0.13 // marks the piece with the given index as filtered // it will not be downloaded void filter_piece(int index, bool filter) const TORRENT_DEPRECATED; @@ -370,6 +377,7 @@ namespace libtorrent // to. void use_interface(const char* net_interface) const; + // deprecated in 0.14 // use save_resume_data() instead. It is async. and // will return the resume data in an alert entry write_resume_data() const TORRENT_DEPRECATED; diff --git a/libtorrent/include/libtorrent/tracker_manager.hpp b/libtorrent/include/libtorrent/tracker_manager.hpp index 580e6d5eb..2d8df1a36 100755 --- a/libtorrent/include/libtorrent/tracker_manager.hpp +++ b/libtorrent/include/libtorrent/tracker_manager.hpp @@ -118,20 +118,21 @@ namespace libtorrent friend class tracker_manager; request_callback(): m_manager(0) {} virtual ~request_callback() {} - virtual void tracker_warning(std::string const& msg) = 0; + virtual void tracker_warning(tracker_request const& req + , std::string const& msg) = 0; virtual void tracker_scrape_response(tracker_request const& req , int complete, int incomplete, int downloads) {} virtual void tracker_response( - tracker_request const& + tracker_request const& req , std::vector& peers , int interval , int complete , int incomplete , address const& external_ip) = 0; virtual void tracker_request_timed_out( - tracker_request const&) = 0; + tracker_request const& req) = 0; virtual void tracker_request_error( - tracker_request const& + tracker_request const& req , int response_code , const std::string& description) = 0; diff --git a/libtorrent/src/bt_peer_connection.cpp b/libtorrent/src/bt_peer_connection.cpp index 7394406dc..b021e8cb1 100755 --- a/libtorrent/src/bt_peer_connection.cpp +++ b/libtorrent/src/bt_peer_connection.cpp @@ -2449,6 +2449,7 @@ namespace libtorrent if (pid == m_ses.get_peer_id()) { + set_failed(); disconnect("closing connection to ourself"); return; } @@ -2465,6 +2466,7 @@ namespace libtorrent // since it most likely is ourself then if (pid == m_ses.get_peer_id()) { + set_failed(); disconnect("closing connection to ourself"); return; } diff --git a/libtorrent/src/http_tracker_connection.cpp b/libtorrent/src/http_tracker_connection.cpp index 3adad2ca1..ea4f0fda0 100755 --- a/libtorrent/src/http_tracker_connection.cpp +++ b/libtorrent/src/http_tracker_connection.cpp @@ -306,7 +306,7 @@ namespace libtorrent entry const* warning = e.find_key("warning message"); if (warning && warning->type() == entry::string_t) { - cb->tracker_warning(warning->string()); + cb->tracker_warning(tracker_req(), warning->string()); } std::vector peer_list; diff --git a/libtorrent/src/policy.cpp b/libtorrent/src/policy.cpp index 02dd5cd93..76d21cc3c 100755 --- a/libtorrent/src/policy.cpp +++ b/libtorrent/src/policy.cpp @@ -384,12 +384,26 @@ namespace libtorrent , "blocked peer removed from peer list")); } } - if (p) p->clear_peer(&i->second); - if (i->second.seed) --m_num_seeds; - m_peers.erase(i++); + erase_peer(i++); } } - + + // any peer that is erased from m_peers will be + // erased through this function. This way we can make + // sure that any references to the peer are removed + // as well, such as in the piece picker. + void policy::erase_peer(iterator i) + { + if (m_torrent->has_picker()) + m_torrent->picker().clear_peer(&i->second); + if (i->second.seed) --m_num_seeds; + if (is_connect_candidate(i->second, m_torrent->is_finished())) + --m_num_connect_candidates; + if (m_round_robin == i) ++m_round_robin; + + m_peers.erase(i); + } + bool policy::is_connect_candidate(peer const& p, bool finished) { if (p.connection @@ -450,16 +464,14 @@ namespace libtorrent policy::iterator policy::find_connect_candidate() { - INVARIANT_CHECK; +// too expensive +// INVARIANT_CHECK; ptime now = time_now(); - ptime min_connect_time(now); iterator candidate = m_peers.end(); int min_reconnect_time = m_torrent->settings().min_reconnect_time; bool finished = m_torrent->is_finished(); - - int min_cidr_distance = (std::numeric_limits::max)(); address external_ip = m_torrent->session().external_address(); // don't bias any particular peers when seeding @@ -472,75 +484,35 @@ namespace libtorrent external_ip = address_v4(bytes); } -#ifndef TORRENT_DISABLE_GEO_IP - int max_inet_as_rate = -1; - bool has_db = m_torrent->session().has_asnum_db(); -#endif + if (m_round_robin == iterator() || m_round_robin == m_peers.end()) + m_round_robin = m_peers.begin(); - int connect_candidates = 0; - int seeds = 0; - for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) + for (int iterations = (std::min)(int(m_peers.size()), 300); + iterations > 0; ++m_round_robin, --iterations) { - if (i->second.seed) ++seeds; - if (!is_connect_candidate(i->second, finished)) continue; - ++connect_candidates; + if (m_round_robin == m_peers.end()) m_round_robin = m_peers.begin(); + + if (!is_connect_candidate(m_round_robin->second, finished)) continue; - // prefer peers with lower failcount if (candidate != m_peers.end() - && candidate->second.failcount < i->second.failcount) + && !compare_peer(candidate->second, m_round_robin->second, external_ip)) continue; + + if (now - m_round_robin->second.connected < + seconds(m_round_robin->second.failcount * min_reconnect_time)) continue; - if (now - i->second.connected < seconds(i->second.failcount * min_reconnect_time)) - continue; - - TORRENT_ASSERT(i->second.connected <= now); - - // don't replace a candidate that is on the local - // network with one that isn't. Local peers should - // always be tried first - if (candidate != m_peers.end() - && is_local(candidate->second.ip.address()) - && !is_local(i->second.ip.address())) - continue; - - if (i->second.connected > min_connect_time) continue; - -#ifndef TORRENT_DISABLE_GEO_IP - if (!finished && has_db) - { - // don't bias fast peers when seeding - std::pair* inet_as = i->second.inet_as; - int peak_rate = inet_as ? inet_as->second : 0; - if (peak_rate <= max_inet_as_rate) continue; - max_inet_as_rate = peak_rate; - } - - if (max_inet_as_rate <= 0) -#endif - { - int distance = cidr_distance(external_ip, i->second.ip.address()); - if (distance > min_cidr_distance) continue; - - min_cidr_distance = distance; - } - - min_connect_time = i->second.connected; - candidate = i; + candidate = m_round_robin; } - m_num_connect_candidates = connect_candidates; - m_num_seeds = seeds; - TORRENT_ASSERT(min_connect_time <= now); - #if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING if (candidate != m_peers.end()) { (*m_torrent->session().m_logger) << time_now_string() << " *** FOUND CONNECTION CANDIDATE [" " ip: " << candidate->second.ip << - " d: " << min_cidr_distance << + " d: " << cidr_distance(external_ip, candidate->second.ip.address()) << " external: " << external_ip << - " t: " << total_seconds(time_now() - min_connect_time) << + " t: " << total_seconds(time_now() - candidate->second.connected) << " ]\n"; } #endif @@ -554,10 +526,6 @@ namespace libtorrent if (m_torrent->is_paused()) return; - piece_picker* p = 0; - if (m_torrent->has_picker()) - p = &m_torrent->picker(); - #ifndef TORRENT_DISABLE_DHT bool pinged = false; #endif @@ -589,9 +557,7 @@ namespace libtorrent && !pe.banned && now - pe.connected > minutes(120)) { - if (p) p->clear_peer(&pe); - if (pe.seed) --m_num_seeds; - m_peers.erase(i++); + erase_peer(i++); } else { @@ -599,52 +565,6 @@ namespace libtorrent } } - // ------------------------------------- - // maintain the number of connections - // ------------------------------------- -/* - // count the number of connected peers except for peers - // that are currently in the process of disconnecting - int num_connected_peers = 0; - - for (iterator i = m_peers.begin(); - i != m_peers.end(); ++i) - { - if (i->connection && !i->connection->is_disconnecting()) - ++num_connected_peers; - } - - if (m_torrent->max_connections() != (std::numeric_limits::max)()) - { - int max_connections = m_torrent->max_connections(); - - if (num_connected_peers >= max_connections) - { - // every minute, disconnect the worst peer in hope of finding a better peer - - ptime local_time = time_now(); - if (m_last_optimistic_disconnect + seconds(120) <= local_time - && find_connect_candidate() != m_peers.end()) - { - m_last_optimistic_disconnect = local_time; - --max_connections; // this will have the effect of disconnecting the worst peer - } - } - else - { - // don't do a disconnect earlier than 1 minute after some peer was connected - m_last_optimistic_disconnect = time_now(); - } - - while (num_connected_peers > max_connections) - { - bool ret = disconnect_one_peer(); - (void)ret; - TORRENT_ASSERT(ret); - --num_connected_peers; - } - } -*/ // ------------------------ // upload shift // ------------------------ @@ -769,6 +689,9 @@ namespace libtorrent "with higher priority, closing"); } } + + if (m_num_connect_candidates > 0) + --m_num_connect_candidates; } else { @@ -797,8 +720,6 @@ namespace libtorrent TORRENT_ASSERT(i->second.connection); if (!c.fast_reconnect()) i->second.connected = time_now(); - if (m_num_connect_candidates > 0) - --m_num_connect_candidates; return true; } @@ -823,10 +744,7 @@ namespace libtorrent p->connection->disconnect("duplicate connection"); return false; } - if (m_torrent->has_picker()) - m_torrent->picker().clear_peer(&i->second); - if (i->second.seed) --m_num_seeds; - m_peers.erase(i); + erase_peer(i); } } else @@ -859,7 +777,8 @@ namespace libtorrent policy::peer* policy::peer_from_tracker(tcp::endpoint const& remote, peer_id const& pid , int src, char flags) { - INVARIANT_CHECK; + // way too expensive + //INVARIANT_CHECK; // just ignore the obviously invalid entries if (remote.address() == address() || remote.port() == 0) @@ -1405,5 +1324,38 @@ namespace libtorrent return prev_amount_upload; } } + + // this returns true if lhs is a better connect candidate than rhs + bool policy::compare_peer(policy::peer const& lhs, policy::peer const& rhs + , address const& external_ip) const + { + // prefer peers with lower failcount + if (lhs.failcount < rhs.failcount) return true; + if (lhs.failcount > rhs.failcount) return false; + + // Local peers should always be tried first + bool lhs_local = is_local(lhs.ip.address()); + bool rhs_local = is_local(rhs.ip.address()); + if (lhs_local && !rhs_local) return true; + if (!lhs_local && rhs_local) return false; + + if (lhs.connected < rhs.connected) return true; + if (lhs.connected > rhs.connected) return false; + +#ifndef TORRENT_DISABLE_GEO_IP + // don't bias fast peers when seeding + if (!m_torrent->is_finished() && m_torrent->session().has_asnum_db()) + { + std::pair* lhs_as = lhs.inet_as; + std::pair* rhs_as = rhs.inet_as; + if (lhs_as ? lhs_as->second : 0 > rhs_as ? rhs->second : 0) return true; + if (lhs_as ? lhs_as->second : 0 < rhs_as ? rhs->second : 0) return false; + } +#endif + int lhs_distance = cidr_distance(external_ip, lhs.ip.address()); + int rhs_distance = cidr_distance(external_ip, rhs.ip.address()); + if (lhs_distance < rhs_distance) return true; + return false; + } } diff --git a/libtorrent/src/session.cpp b/libtorrent/src/session.cpp index 1d54bc40d..f6ee4a2bb 100755 --- a/libtorrent/src/session.cpp +++ b/libtorrent/src/session.cpp @@ -239,6 +239,10 @@ namespace libtorrent return m_impl->find_torrent_handle(info_hash); } + torrent_handle session::add_torrent(add_torrent_params const& params) + { + return m_impl->add_torrent(params); + } // if the torrent already exists, this will throw duplicate_torrent torrent_handle session::add_torrent( @@ -251,8 +255,13 @@ namespace libtorrent { TORRENT_ASSERT(!ti.m_half_metadata); boost::intrusive_ptr tip(new torrent_info(ti)); - return m_impl->add_torrent(tip, save_path, resume_data - , storage_mode, sc, paused, 0); + add_torrent_params p(sc); + p.ti = tip; + p.save_path = save_path; + p.resume_data = &resume_data; + p.storage_mode = storage_mode; + p.paused = paused; + return m_impl->add_torrent(p); } torrent_handle session::add_torrent( @@ -265,8 +274,14 @@ namespace libtorrent , void* userdata) { TORRENT_ASSERT(!ti->m_half_metadata); - return m_impl->add_torrent(ti, save_path, resume_data - , storage_mode, sc, paused, userdata); + add_torrent_params p(sc); + p.ti = ti; + p.save_path = save_path; + p.resume_data = &resume_data; + p.storage_mode = storage_mode; + p.paused = paused; + p.userdata = userdata; + return m_impl->add_torrent(p); } torrent_handle session::add_torrent( @@ -280,8 +295,13 @@ namespace libtorrent , storage_constructor_type sc , void* userdata) { - return m_impl->add_torrent(tracker_url, info_hash, name, save_path, e - , storage_mode, sc, paused, userdata); + add_torrent_params p(sc); + p.tracker_url = tracker_url; + p.info_hash = info_hash; + p.save_path = save_path; + p.paused = paused; + p.userdata = userdata; + return m_impl->add_torrent(p); } void session::remove_torrent(const torrent_handle& h, int options) diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index e8b34c9bb..ae07a940a 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -74,6 +74,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/kademlia/dht_tracker.hpp" #include "libtorrent/enum_net.hpp" +#include "libtorrent/config.hpp" #ifndef TORRENT_DISABLE_ENCRYPTION @@ -158,6 +159,7 @@ namespace aux { , m_max_connections(200) , m_num_unchoked(0) , m_unchoke_time_scaler(0) + , m_auto_manage_time_scaler(0) , m_optimistic_unchoke_time_scaler(0) , m_disconnect_time_scaler(90) , m_incoming_connection(false) @@ -165,6 +167,7 @@ namespace aux { #ifndef TORRENT_DISABLE_DHT , m_dht_same_port(true) , m_external_udp_port(0) + , m_torrent_sequence(0) , m_dht_socket(m_io_service, bind(&session_impl::on_receive_udp, this, _1, _2, _3, _4) , m_half_open) #endif @@ -762,7 +765,7 @@ namespace aux { + boost::lexical_cast(ep) + "' " + e.message(); (*m_logger) << msg << "\n"; #endif -#ifdef _WIN32 +#ifdef TORRENT_WINDOWS // Windows sometimes generates this error. It seems to be // non-fatal and we have to do another async_accept. if (e.value() == ERROR_SEM_TIMEOUT) @@ -1035,7 +1038,7 @@ namespace aux { { m_alerts.post_alert( tracker_announce_alert( - t.get_handle(), "tracker announce")); + t.get_handle(), req.url, "tracker announce")); } } @@ -1173,6 +1176,16 @@ namespace aux { c.keep_alive(); } + // -------------------------------------------------------------- + // auto managed torrent + // -------------------------------------------------------------- + m_auto_manage_time_scaler--; + if (m_auto_manage_time_scaler <= 0) + { + m_auto_manage_time_scaler = settings().auto_manage_interval; + recalculate_auto_managed_torrents(); + } + // -------------------------------------------------------------- // unchoke set and optimistic unchoke calculations // -------------------------------------------------------------- @@ -1180,173 +1193,8 @@ namespace aux { if (m_unchoke_time_scaler <= 0 && !m_connections.empty()) { m_unchoke_time_scaler = settings().unchoke_interval; - - std::vector peers; - for (connection_map::iterator i = m_connections.begin() - , end(m_connections.end()); i != end; ++i) - { - peer_connection* p = i->get(); - torrent* t = p->associated_torrent().lock().get(); - if (!p->peer_info_struct() - || t == 0 - || !p->is_peer_interested() - || p->is_disconnecting() - || p->is_connecting() - || (p->share_diff() < -free_upload_amount - && !t->is_seed())) - { - if (!(*i)->is_choked() && t) - { - policy::peer* pi = p->peer_info_struct(); - if (pi && pi->optimistically_unchoked) - { - pi->optimistically_unchoked = false; - // force a new optimistic unchoke - m_optimistic_unchoke_time_scaler = 0; - } - t->choke_peer(*(*i)); - } - continue; - } - peers.push_back(i->get()); - } - - // sorts the peers that are eligible for unchoke by download rate and secondary - // by total upload. The reason for this is, if all torrents are being seeded, - // the download rate will be 0, and the peers we have sent the least to should - // be unchoked - std::sort(peers.begin(), peers.end() - , bind(&peer_connection::unchoke_compare, _1, _2)); - - std::for_each(m_connections.begin(), m_connections.end() - , bind(&peer_connection::reset_choke_counters, _1)); - - // auto unchoke - int upload_limit = m_bandwidth_manager[peer_connection::upload_channel]->throttle(); - if (m_settings.auto_upload_slots && upload_limit != bandwidth_limit::inf) - { - // if our current upload rate is less than 90% of our - // limit AND most torrents are not "congested", i.e. - // they are not holding back because of a per-torrent - // limit - if (m_stat.upload_rate() < upload_limit * 0.9f - && m_allowed_upload_slots <= m_num_unchoked + 1 - && congested_torrents < uncongested_torrents) - { - ++m_allowed_upload_slots; - } - else if (m_upload_channel.queue_size() > 1 - && m_allowed_upload_slots > m_max_uploads) - { - --m_allowed_upload_slots; - } - } - - // reserve one upload slot for optimistic unchokes - int unchoke_set_size = m_allowed_upload_slots - 1; - - m_num_unchoked = 0; - // go through all the peers and unchoke the first ones and choke - // all the other ones. - for (std::vector::iterator i = peers.begin() - , end(peers.end()); i != end; ++i) - { - peer_connection* p = *i; - TORRENT_ASSERT(p); - torrent* t = p->associated_torrent().lock().get(); - TORRENT_ASSERT(t); - if (unchoke_set_size > 0) - { - if (p->is_choked()) - { - if (!t->unchoke_peer(*p)) - continue; - } - - --unchoke_set_size; - ++m_num_unchoked; - - TORRENT_ASSERT(p->peer_info_struct()); - if (p->peer_info_struct()->optimistically_unchoked) - { - // force a new optimistic unchoke - m_optimistic_unchoke_time_scaler = 0; - p->peer_info_struct()->optimistically_unchoked = false; - } - } - else - { - TORRENT_ASSERT(p->peer_info_struct()); - if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked) - t->choke_peer(*p); - if (!p->is_choked()) - ++m_num_unchoked; - } - } - - m_optimistic_unchoke_time_scaler--; - if (m_optimistic_unchoke_time_scaler <= 0) - { - m_optimistic_unchoke_time_scaler - = settings().optimistic_unchoke_multiplier; - - // find the peer that has been waiting the longest to be optimistically - // unchoked - connection_map::iterator current_optimistic_unchoke = m_connections.end(); - connection_map::iterator optimistic_unchoke_candidate = m_connections.end(); - ptime last_unchoke = max_time(); - - for (connection_map::iterator i = m_connections.begin() - , end(m_connections.end()); i != end; ++i) - { - peer_connection* p = i->get(); - TORRENT_ASSERT(p); - policy::peer* pi = p->peer_info_struct(); - if (!pi) continue; - torrent* t = p->associated_torrent().lock().get(); - if (!t) continue; - - if (pi->optimistically_unchoked) - { - TORRENT_ASSERT(!p->is_choked()); - TORRENT_ASSERT(current_optimistic_unchoke == m_connections.end()); - current_optimistic_unchoke = i; - } - - if (pi->last_optimistically_unchoked < last_unchoke - && !p->is_connecting() - && !p->is_disconnecting() - && p->is_peer_interested() - && t->free_upload_slots() - && p->is_choked()) - { - last_unchoke = pi->last_optimistically_unchoked; - optimistic_unchoke_candidate = i; - } - } - - if (optimistic_unchoke_candidate != m_connections.end() - && optimistic_unchoke_candidate != current_optimistic_unchoke) - { - if (current_optimistic_unchoke != m_connections.end()) - { - torrent* t = (*current_optimistic_unchoke)->associated_torrent().lock().get(); - TORRENT_ASSERT(t); - (*current_optimistic_unchoke)->peer_info_struct()->optimistically_unchoked = false; - t->choke_peer(*current_optimistic_unchoke->get()); - } - else - { - ++m_num_unchoked; - } - - torrent* t = (*optimistic_unchoke_candidate)->associated_torrent().lock().get(); - TORRENT_ASSERT(t); - bool ret = t->unchoke_peer(*optimistic_unchoke_candidate->get()); - TORRENT_ASSERT(ret); - (*optimistic_unchoke_candidate)->peer_info_struct()->optimistically_unchoked = true; - } - } + recalculate_unchoke_slots(congested_torrents + , uncongested_torrents); } // -------------------------------------------------------------- @@ -1357,7 +1205,7 @@ namespace aux { { m_disconnect_time_scaler = 90; - // every 60 seconds, disconnect the worst peer + // every 90 seconds, disconnect the worst peer // if we have reached the connection limit if (num_connections() >= max_connections() && !m_torrents.empty()) { @@ -1372,6 +1220,269 @@ namespace aux { } } + void session_impl::recalculate_auto_managed_torrents() + { + // these vectors are filled with auto managed torrents + std::vector downloaders; + downloaders.reserve(m_torrents.size()); + std::vector seeds; + seeds.reserve(m_torrents.size()); + + // these counters are set to the number of torrents + // of each kind we're allowed to have active + int num_downloaders = settings().active_downloads; + int num_seeds = settings().active_seeds; + + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent* t = i->second.get(); + TORRENT_ASSERT(t); + if (t->is_auto_managed()) + { + // this torrent is auto managed, add it to + // the list (depending on if it's a seed or not) + if (t->is_finished()) + seeds.push_back(t); + else + downloaders.push_back(t); + } + else if (!t->is_paused()) + { + // this is not an auto managed torrent, + // if it's running, decrease the respective + // counters. + if (t->is_finished()) + { + --num_seeds; + --num_downloaders; + } + else + { + --num_downloaders; + } + } + } + + bool handled_by_extension = false; + +#ifndef TORRENT_DISABLE_EXTENSIONS + // TODO: allow extensions to sort torrents for queuing +#endif + + if (!handled_by_extension) + { + std::sort(downloaders.begin(), downloaders.end() + , bind(&torrent::sequence_number, _1) < bind(&torrent::sequence_number, _2)); + + std::sort(seeds.begin(), seeds.end() + , bind(&torrent::seed_cycles_int, _1, boost::ref(m_settings)) + < bind(&torrent::seed_cycles_int, _2, boost::ref(m_settings))); + } + + for (std::vector::iterator i = downloaders.begin() + , end(downloaders.end()); i != end; ++i) + { + torrent* t = *i; + if (num_downloaders > 0) + { + --num_downloaders; + if (t->is_paused()) t->resume(); + } + else + { + if (!t->is_paused()) t->pause(); + } + } + + for (std::vector::iterator i = seeds.begin() + , end(seeds.end()); i != end; ++i) + { + torrent* t = *i; + if (num_downloaders > 0 && num_seeds > 0) + { + --num_downloaders; + --num_seeds; + if (t->is_paused()) t->resume(); + } + else + { + if (!t->is_paused()) t->pause(); + } + } + } + + void session_impl::recalculate_unchoke_slots(int congested_torrents + , int uncongested_torrents) + { + std::vector peers; + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = i->get(); + torrent* t = p->associated_torrent().lock().get(); + if (!p->peer_info_struct() + || t == 0 + || !p->is_peer_interested() + || p->is_disconnecting() + || p->is_connecting() + || (p->share_diff() < -free_upload_amount + && !t->is_seed())) + { + if (!(*i)->is_choked() && t) + { + policy::peer* pi = p->peer_info_struct(); + if (pi && pi->optimistically_unchoked) + { + pi->optimistically_unchoked = false; + // force a new optimistic unchoke + m_optimistic_unchoke_time_scaler = 0; + } + t->choke_peer(*(*i)); + } + continue; + } + peers.push_back(i->get()); + } + + // sorts the peers that are eligible for unchoke by download rate and secondary + // by total upload. The reason for this is, if all torrents are being seeded, + // the download rate will be 0, and the peers we have sent the least to should + // be unchoked + std::sort(peers.begin(), peers.end() + , bind(&peer_connection::unchoke_compare, _1, _2)); + + std::for_each(m_connections.begin(), m_connections.end() + , bind(&peer_connection::reset_choke_counters, _1)); + + // auto unchoke + int upload_limit = m_bandwidth_manager[peer_connection::upload_channel]->throttle(); + if (m_settings.auto_upload_slots && upload_limit != bandwidth_limit::inf) + { + // if our current upload rate is less than 90% of our + // limit AND most torrents are not "congested", i.e. + // they are not holding back because of a per-torrent + // limit + if (m_stat.upload_rate() < upload_limit * 0.9f + && m_allowed_upload_slots <= m_num_unchoked + 1 + && congested_torrents < uncongested_torrents) + { + ++m_allowed_upload_slots; + } + else if (m_upload_channel.queue_size() > 1 + && m_allowed_upload_slots > m_max_uploads) + { + --m_allowed_upload_slots; + } + } + + // reserve one upload slot for optimistic unchokes + int unchoke_set_size = m_allowed_upload_slots - 1; + + m_num_unchoked = 0; + // go through all the peers and unchoke the first ones and choke + // all the other ones. + for (std::vector::iterator i = peers.begin() + , end(peers.end()); i != end; ++i) + { + peer_connection* p = *i; + TORRENT_ASSERT(p); + torrent* t = p->associated_torrent().lock().get(); + TORRENT_ASSERT(t); + if (unchoke_set_size > 0) + { + if (p->is_choked()) + { + if (!t->unchoke_peer(*p)) + continue; + } + + --unchoke_set_size; + ++m_num_unchoked; + + TORRENT_ASSERT(p->peer_info_struct()); + if (p->peer_info_struct()->optimistically_unchoked) + { + // force a new optimistic unchoke + m_optimistic_unchoke_time_scaler = 0; + p->peer_info_struct()->optimistically_unchoked = false; + } + } + else + { + TORRENT_ASSERT(p->peer_info_struct()); + if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked) + t->choke_peer(*p); + if (!p->is_choked()) + ++m_num_unchoked; + } + } + + m_optimistic_unchoke_time_scaler--; + if (m_optimistic_unchoke_time_scaler <= 0) + { + m_optimistic_unchoke_time_scaler + = settings().optimistic_unchoke_multiplier; + + // find the peer that has been waiting the longest to be optimistically + // unchoked + connection_map::iterator current_optimistic_unchoke = m_connections.end(); + connection_map::iterator optimistic_unchoke_candidate = m_connections.end(); + ptime last_unchoke = max_time(); + + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + peer_connection* p = i->get(); + TORRENT_ASSERT(p); + policy::peer* pi = p->peer_info_struct(); + if (!pi) continue; + torrent* t = p->associated_torrent().lock().get(); + if (!t) continue; + + if (pi->optimistically_unchoked) + { + TORRENT_ASSERT(!p->is_choked()); + TORRENT_ASSERT(current_optimistic_unchoke == m_connections.end()); + current_optimistic_unchoke = i; + } + + if (pi->last_optimistically_unchoked < last_unchoke + && !p->is_connecting() + && !p->is_disconnecting() + && p->is_peer_interested() + && t->free_upload_slots() + && p->is_choked()) + { + last_unchoke = pi->last_optimistically_unchoked; + optimistic_unchoke_candidate = i; + } + } + + if (optimistic_unchoke_candidate != m_connections.end() + && optimistic_unchoke_candidate != current_optimistic_unchoke) + { + if (current_optimistic_unchoke != m_connections.end()) + { + torrent* t = (*current_optimistic_unchoke)->associated_torrent().lock().get(); + TORRENT_ASSERT(t); + (*current_optimistic_unchoke)->peer_info_struct()->optimistically_unchoked = false; + t->choke_peer(*current_optimistic_unchoke->get()); + } + else + { + ++m_num_unchoked; + } + + torrent* t = (*optimistic_unchoke_candidate)->associated_torrent().lock().get(); + TORRENT_ASSERT(t); + bool ret = t->unchoke_peer(*optimistic_unchoke_candidate->get()); + TORRENT_ASSERT(ret); + (*optimistic_unchoke_candidate)->peer_info_struct()->optimistically_unchoked = true; + } + } + } + void session_impl::operator()() { eh_initializer(); @@ -1476,18 +1587,11 @@ namespace aux { return torrent_handle(find_torrent(info_hash)); } - torrent_handle session_impl::add_torrent( - boost::intrusive_ptr ti - , fs::path const& save_path - , entry const& resume_data - , storage_mode_t storage_mode - , storage_constructor_type sc - , bool paused - , void* userdata) + torrent_handle session_impl::add_torrent(add_torrent_params const& params) { - TORRENT_ASSERT(!save_path.empty()); + TORRENT_ASSERT(!params.save_path.empty()); - if (ti->begin_files() == ti->end_files()) + if (params.ti && params.ti->num_files() == 0) { #ifndef BOOST_NO_EXCEPTIONS throw std::runtime_error("no files in torrent"); @@ -1510,9 +1614,18 @@ namespace aux { #endif } + // figure out the info hash of the torrent + sha1_hash const* ih = 0; + if (params.ti) ih = ¶ms.ti->info_hash(); + else ih = ¶ms.info_hash; + // is the torrent already active? - if (!find_torrent(ti->info_hash()).expired()) + boost::shared_ptr torrent_ptr = find_torrent(*ih).lock(); + if (torrent_ptr) { + if (!params.duplicate_is_error) + return torrent_handle(torrent_ptr); + #ifndef BOOST_NO_EXCEPTIONS throw duplicate_torrent(); #else @@ -1523,25 +1636,36 @@ namespace aux { // create the torrent and the data associated with // the checker thread and store it before starting // the thread - boost::shared_ptr torrent_ptr( - new torrent(*this, ti, save_path - , m_listen_interface, storage_mode, 16 * 1024 - , sc, paused, resume_data)); + if (params.ti) + { + torrent_ptr.reset(new torrent(*this, params.ti, params.save_path + , m_listen_interface, params.storage_mode, 16 * 1024 + , params.storage, params.paused, params.resume_data + , m_torrent_sequence, params.auto_managed)); + } + else + { + torrent_ptr.reset(new torrent(*this, params.tracker_url, *ih, params.name + , params.save_path, m_listen_interface, params.storage_mode, 16 * 1024 + , params.storage, params.paused, params.resume_data + , m_torrent_sequence, params.auto_managed)); + } torrent_ptr->start(); + ++m_torrent_sequence; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) { - boost::shared_ptr tp((*i)(torrent_ptr.get(), userdata)); + boost::shared_ptr tp((*i)(torrent_ptr.get(), params.userdata)); if (tp) torrent_ptr->add_extension(tp); } #endif #ifndef TORRENT_DISABLE_DHT - if (m_dht) + if (m_dht && params.ti) { - torrent_info::nodes_t const& nodes = ti->nodes(); + torrent_info::nodes_t const& nodes = params.ti->nodes(); std::for_each(nodes.begin(), nodes.end(), bind( (void(dht::dht_tracker::*)(std::pair const&)) &dht::dht_tracker::add_node @@ -1549,7 +1673,7 @@ namespace aux { } #endif - m_torrents.insert(std::make_pair(ti->info_hash(), torrent_ptr)); + m_torrents.insert(std::make_pair(*ih, torrent_ptr)); return torrent_handle(torrent_ptr); } @@ -1568,62 +1692,6 @@ namespace aux { m_queued_for_checking.front()->start_checking(); } - torrent_handle session_impl::add_torrent( - char const* tracker_url - , sha1_hash const& info_hash - , char const* name - , fs::path const& save_path - , entry const& resume_data - , storage_mode_t storage_mode - , storage_constructor_type sc - , bool paused - , void* userdata) - { - - // TODO: support resume data in this case - TORRENT_ASSERT(!save_path.empty()); - - // lock the session - session_impl::mutex_t::scoped_lock l(m_mutex); - -// INVARIANT_CHECK; - - // is the torrent already active? - if (!find_torrent(info_hash).expired()) - { -#ifndef BOOST_NO_EXCEPTIONS - throw duplicate_torrent(); -#else - return torrent_handle(); -#endif - } - - // you cannot add new torrents to a session that is closing down - TORRENT_ASSERT(!is_aborted()); - - // create the torrent and the data associated with - // the checker thread and store it before starting - // the thread - boost::shared_ptr torrent_ptr( - new torrent(*this, tracker_url, info_hash, name - , save_path, m_listen_interface, storage_mode, 16 * 1024 - , sc, paused, resume_data)); - torrent_ptr->start(); - -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - boost::shared_ptr tp((*i)(torrent_ptr.get(), userdata)); - if (tp) torrent_ptr->add_extension(tp); - } -#endif - - m_torrents.insert(std::make_pair(info_hash, torrent_ptr)); - - return torrent_handle(torrent_ptr); - } - void session_impl::remove_torrent(const torrent_handle& h, int options) { boost::shared_ptr tptr = h.m_torrent.lock(); @@ -1672,7 +1740,7 @@ namespace aux { { m_alerts.post_alert( tracker_announce_alert( - t.get_handle(), "tracker announce, event=stopped")); + t.get_handle(), req.url, "tracker announce, event=stopped")); } } #ifndef NDEBUG diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index 48a466a9c..51f416add 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -157,11 +157,14 @@ namespace libtorrent , int block_size , storage_constructor_type sc , bool paused - , entry const& resume_data) + , entry const* resume_data + , int seq + , bool auto_managed) : m_torrent_file(tf) , m_abort(false) , m_paused(paused) , m_just_paused(false) + , m_auto_managed(auto_managed) , m_event(tracker_request::started) , m_block_size(0) , m_storage(0) @@ -196,7 +199,6 @@ namespace libtorrent , m_storage_mode(storage_mode) , m_state(torrent_status::queued_for_checking) , m_progress(0.f) - , m_resume_data(resume_data) , m_default_block_size(block_size) , m_connections_initialized(true) , m_settings(ses.settings()) @@ -206,11 +208,13 @@ namespace libtorrent , m_max_connections((std::numeric_limits::max)()) , m_deficit_counter(0) , m_policy(this) + , m_sequence_number(seq) , m_active_time(seconds(0)) , m_seeding_time(seconds(0)) , m_total_uploaded(0) , m_total_downloaded(0) { + if (resume_data) m_resume_data = *resume_data; #ifndef NDEBUG m_files_checked = false; #endif @@ -227,11 +231,14 @@ namespace libtorrent , int block_size , storage_constructor_type sc , bool paused - , entry const& resume_data) + , entry const* resume_data + , int seq + , bool auto_managed) : m_torrent_file(new torrent_info(info_hash)) , m_abort(false) , m_paused(paused) , m_just_paused(false) + , m_auto_managed(auto_managed) , m_event(tracker_request::started) , m_block_size(0) , m_storage(0) @@ -265,7 +272,6 @@ namespace libtorrent , m_storage_mode(storage_mode) , m_state(torrent_status::queued_for_checking) , m_progress(0.f) - , m_resume_data(resume_data) , m_default_block_size(block_size) , m_connections_initialized(false) , m_settings(ses.settings()) @@ -275,11 +281,13 @@ namespace libtorrent , m_max_connections((std::numeric_limits::max)()) , m_deficit_counter(0) , m_policy(this) + , m_sequence_number(seq) , m_active_time(seconds(0)) , m_seeding_time(seconds(0)) , m_total_uploaded(0) , m_total_downloaded(0) { + if (resume_data) m_resume_data = *resume_data; #ifndef NDEBUG m_files_checked = false; #endif @@ -453,6 +461,7 @@ namespace libtorrent } std::fill(m_have_pieces.begin(), m_have_pieces.end(), false); m_num_pieces = 0; + auto_managed(false); pause(); return; } @@ -633,6 +642,7 @@ namespace libtorrent } std::fill(m_have_pieces.begin(), m_have_pieces.end(), false); m_num_pieces = 0; + auto_managed(false); pause(); m_ses.done_checking(shared_from_this()); return; @@ -727,7 +737,7 @@ namespace libtorrent if (m_ses.m_alerts.should_post(alert::info)) { m_ses.m_alerts.post_alert(tracker_reply_alert( - get_handle(), peers.size(), "Got peers from DHT")); + get_handle(), peers.size(), "DHT", "Got peers from DHT")); } std::for_each(peers.begin(), peers.end(), bind( &policy::peer_from_tracker, boost::ref(m_policy), _1, peer_id(0) @@ -767,7 +777,7 @@ namespace libtorrent return !m_paused && m_next_request < time_now(); } - void torrent::tracker_warning(std::string const& msg) + void torrent::tracker_warning(tracker_request const& req, std::string const& msg) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); @@ -775,7 +785,7 @@ namespace libtorrent if (m_ses.m_alerts.should_post(alert::warning)) { - m_ses.m_alerts.post_alert(tracker_warning_alert(get_handle(), msg)); + m_ses.m_alerts.post_alert(tracker_warning_alert(get_handle(), req.url, msg)); } } @@ -792,10 +802,8 @@ namespace libtorrent if (m_ses.m_alerts.should_post(alert::info)) { - std::stringstream s; - s << "Got scrape response from tracker: " << req.url; m_ses.m_alerts.post_alert(scrape_reply_alert( - get_handle(), m_incomplete, m_complete, s.str())); + get_handle(), m_incomplete, m_complete, req.url, "got scrape response from tracker")); } } @@ -891,10 +899,8 @@ namespace libtorrent if (m_ses.m_alerts.should_post(alert::info)) { - std::stringstream s; - s << "Got response from tracker: " << r.url; m_ses.m_alerts.post_alert(tracker_reply_alert( - get_handle(), peer_list.size(), s.str())); + get_handle(), peer_list.size(), r.url, "got response from tracker")); } m_got_tracker_response = true; } @@ -3268,6 +3274,42 @@ namespace libtorrent } } + void torrent::auto_managed(bool a) + { + INVARIANT_CHECK; + + if (m_auto_managed == a) return; + m_auto_managed = a; + // recalculate which torrents should be + // paused + m_ses.m_auto_manage_time_scaler = 0; + } + + float torrent::seed_cycles(session_settings const& s) const + { + if (!is_seed()) return 0.f; + + int seeding = total_seconds(m_seeding_time); + int downloading = total_seconds(m_active_time) - seeding; + + // if the seed time limit is set to less than 60 minutes + // use 60 minutes, to avoid oscillation + float ret = seeding / float((std::max)(s.seed_time_limit, 60 * 60)); + + // if it took less than 30 minutes to download, disregard the + // seed_time_ratio_limit, since it would make it oscillate too frequent + if (downloading > 30 * 60 && s.seed_time_ratio_limit >= 1.f) + ret = (std::max)(ret, (seeding / downloading + / s.seed_time_ratio_limit)); + + size_type downloaded = (std::max)(m_total_downloaded, m_torrent_file->total_size()); + if (downloaded > 0 && s.share_ratio_limit >= 1.f) + ret = (std::max)(ret, float(m_total_uploaded) / downloaded + / s.share_ratio_limit); + + return ret; + } + // this is an async operation triggered by the client void torrent::save_resume_data() { @@ -3612,6 +3654,7 @@ namespace libtorrent st.list_peers = m_policy.num_peers(); st.list_seeds = m_policy.num_seeds(); st.connect_candidates = m_policy.num_connect_candidates(); + st.seed_cycles = seed_cycles(m_ses.m_settings); st.all_time_upload = m_total_uploaded; st.all_time_download = m_total_downloaded; @@ -3740,16 +3783,14 @@ namespace libtorrent if (m_ses.m_alerts.should_post(alert::warning)) { - std::stringstream s; - s << "tracker: \"" << r.url << "\" timed out"; if (r.kind == tracker_request::announce_request) { - m_ses.m_alerts.post_alert(tracker_alert(get_handle() - , m_failed_trackers + 1, 0, s.str())); + m_ses.m_alerts.post_alert(tracker_error_alert(get_handle() + , m_failed_trackers + 1, 0, r.url, "tracker timed out")); } else if (r.kind == tracker_request::scrape_request) { - m_ses.m_alerts.post_alert(scrape_failed_alert(get_handle(), s.str())); + m_ses.m_alerts.post_alert(scrape_failed_alert(get_handle(), r.url, "tracker timed out")); } } @@ -3772,16 +3813,14 @@ namespace libtorrent #endif if (m_ses.m_alerts.should_post(alert::warning)) { - std::stringstream s; - s << "tracker: \"" << r.url << "\" " << str; if (r.kind == tracker_request::announce_request) { - m_ses.m_alerts.post_alert(tracker_alert(get_handle() - , m_failed_trackers + 1, response_code, s.str())); + m_ses.m_alerts.post_alert(tracker_error_alert(get_handle() + , m_failed_trackers + 1, response_code, r.url, str)); } else if (r.kind == tracker_request::scrape_request) { - m_ses.m_alerts.post_alert(scrape_failed_alert(get_handle(), s.str())); + m_ses.m_alerts.post_alert(scrape_failed_alert(get_handle(), r.url, str)); } } diff --git a/libtorrent/src/torrent_handle.cpp b/libtorrent/src/torrent_handle.cpp index ad827412d..79cac2e7d 100755 --- a/libtorrent/src/torrent_handle.cpp +++ b/libtorrent/src/torrent_handle.cpp @@ -265,6 +265,18 @@ namespace libtorrent TORRENT_FORWARD(resume()); } + bool torrent_handle::is_auto_managed() const + { + INVARIANT_CHECK; + TORRENT_FORWARD_RETURN(is_auto_managed(), true); + } + + void torrent_handle::auto_managed(bool m) const + { + INVARIANT_CHECK; + TORRENT_FORWARD(auto_managed(m)); + } + void torrent_handle::set_tracker_login(std::string const& name , std::string const& password) const { diff --git a/libtorrent/src/udp_tracker_connection.cpp b/libtorrent/src/udp_tracker_connection.cpp index a6320786f..1eda73841 100755 --- a/libtorrent/src/udp_tracker_connection.cpp +++ b/libtorrent/src/udp_tracker_connection.cpp @@ -144,7 +144,7 @@ namespace libtorrent { std::string tracker_address_type = target_address.address().is_v4() ? "IPv4" : "IPv6"; std::string bind_address_type = bind_interface().is_v4() ? "IPv4" : "IPv6"; - cb->tracker_warning("the tracker only resolves to an " + cb->tracker_warning(tracker_req(), "the tracker only resolves to an " + tracker_address_type + " address, and you're listening on an " + bind_address_type + " socket. This may prevent you from receiving incoming connections."); }