diff --git a/libtorrent/include/libtorrent/alert_types.hpp b/libtorrent/include/libtorrent/alert_types.hpp index e6ade9a26..48491bca4 100755 --- a/libtorrent/include/libtorrent/alert_types.hpp +++ b/libtorrent/include/libtorrent/alert_types.hpp @@ -261,17 +261,6 @@ namespace libtorrent { return std::auto_ptr(new torrent_paused_alert(*this)); } }; - struct TORRENT_EXPORT torrent_checked_alert: torrent_alert - { - torrent_checked_alert(torrent_handle const& h, std::string const& msg) - : torrent_alert(h, alert::info, msg) - {} - - virtual std::auto_ptr clone() const - { return std::auto_ptr(new torrent_checked_alert(*this)); } - }; - - struct TORRENT_EXPORT url_seed_alert: torrent_alert { url_seed_alert( diff --git a/libtorrent/include/libtorrent/allocate_resources.hpp b/libtorrent/include/libtorrent/allocate_resources.hpp new file mode 100644 index 000000000..3d8237914 --- /dev/null +++ b/libtorrent/include/libtorrent/allocate_resources.hpp @@ -0,0 +1,78 @@ +/* + +Copyright (c) 2003, Magnus Jonsson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED +#define TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED + +#include +#include + +#include + +#include "libtorrent/resource_request.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/session.hpp" + +namespace libtorrent +{ + class peer_connection; + class torrent; + + int saturated_add(int a, int b); + + // Function to allocate a limited resource fairly among many consumers. + // It takes into account the current use, and the consumer's desired use. + // Should be invoked periodically to allow it adjust to the situation (make + // sure "used" is updated between calls!). + // If resources = std::numeric_limits::max() it means there is an infinite + // supply of resources (so everyone can get what they want). + + void allocate_resources( + int resources + , std::map >& torrents + , resource_request torrent::* res); + + void allocate_resources( + int resources + , std::map& connections + , resource_request peer_connection::* res); + + // Used for global limits. + void allocate_resources( + int resources + , std::vector& _sessions + , resource_request session::* res); +} + + +#endif diff --git a/libtorrent/include/libtorrent/bt_peer_connection.hpp b/libtorrent/include/libtorrent/bt_peer_connection.hpp index 0fcba89a8..4f13b2fcf 100755 --- a/libtorrent/include/libtorrent/bt_peer_connection.hpp +++ b/libtorrent/include/libtorrent/bt_peer_connection.hpp @@ -65,6 +65,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent.hpp" +#include "libtorrent/allocate_resources.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/config.hpp" diff --git a/libtorrent/include/libtorrent/peer_connection.hpp b/libtorrent/include/libtorrent/peer_connection.hpp index 8dfc2b805..976e03794 100755 --- a/libtorrent/include/libtorrent/peer_connection.hpp +++ b/libtorrent/include/libtorrent/peer_connection.hpp @@ -64,6 +64,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent.hpp" +#include "libtorrent/allocate_resources.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/config.hpp" @@ -212,7 +213,7 @@ namespace libtorrent void add_stat(size_type downloaded, size_type uploaded); // is called once every second by the main loop - void second_tick(float tick_interval) throw(); + void second_tick(float tick_interval); boost::shared_ptr get_socket() const { return m_socket; } tcp::endpoint const& remote() const { return m_remote; } diff --git a/libtorrent/include/libtorrent/policy.hpp b/libtorrent/include/libtorrent/policy.hpp index 3f192ff51..6c976d047 100755 --- a/libtorrent/include/libtorrent/policy.hpp +++ b/libtorrent/include/libtorrent/policy.hpp @@ -155,13 +155,6 @@ namespace libtorrent // this is true if the peer is a seed bool seed; - // true if this peer currently is unchoked - // because of an optimistic unchoke. - // when the optimistic unchoke is moved to - // another peer, this peer will be choked - // if this is true - bool optimistically_unchoked; - // the time when this peer was optimistically unchoked // the last time. libtorrent::ptime last_optimistically_unchoked; @@ -210,18 +203,25 @@ namespace libtorrent peer_connection* connection; }; - int num_peers() const { return m_peers.size(); } + int num_peers() const + { + return m_peers.size(); + } + int num_uploads() const + { + return m_num_unchoked; + } + typedef std::list::iterator iterator; typedef std::list::const_iterator const_iterator; iterator begin_peer() { return m_peers.begin(); } iterator end_peer() { return m_peers.end(); } bool connect_one_peer(); - bool disconnect_one_peer(); private: -/* + bool unchoke_one_peer(); void choke_one_peer(); iterator find_choke_candidate(); @@ -233,7 +233,8 @@ namespace libtorrent void seed_choke_one_peer(); iterator find_seed_choke_candidate(); iterator find_seed_unchoke_candidate(); -*/ + + bool disconnect_one_peer(); iterator find_disconnect_candidate(); iterator find_connect_candidate(); @@ -241,6 +242,10 @@ namespace libtorrent torrent* m_torrent; + // the number of unchoked peers + // at any given time + int m_num_unchoked; + // free download we have got that hasn't // been distributed yet. size_type m_available_free_upload; @@ -248,7 +253,7 @@ namespace libtorrent // if there is a connection limit, // we disconnect one peer every minute in hope of // establishing a connection with a better peer -// ptime m_last_optimistic_disconnect; + ptime m_last_optimistic_disconnect; }; } diff --git a/libtorrent/include/libtorrent/session.hpp b/libtorrent/include/libtorrent/session.hpp index a123314ed..f721b3293 100755 --- a/libtorrent/include/libtorrent/session.hpp +++ b/libtorrent/include/libtorrent/session.hpp @@ -61,6 +61,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/version.hpp" #include "libtorrent/fingerprint.hpp" +#include "libtorrent/resource_request.hpp" #include "libtorrent/storage.hpp" #ifdef _MSC_VER @@ -265,6 +266,12 @@ namespace libtorrent void stop_natpmp(); void stop_upnp(); + // Resource management used for global limits. + resource_request m_ul_bandwidth_quota; + resource_request m_dl_bandwidth_quota; + resource_request m_uploads_quota; + resource_request m_connections_quota; + private: // just a way to initialize boost.filesystem diff --git a/libtorrent/include/libtorrent/session_impl.hpp b/libtorrent/include/libtorrent/session_impl.hpp new file mode 100644 index 000000000..67c3fef1d --- /dev/null +++ b/libtorrent/include/libtorrent/session_impl.hpp @@ -0,0 +1,594 @@ +/* + +Copyright (c) 2006, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_SESSION_IMPL_HPP_INCLUDED +#define TORRENT_SESSION_IMPL_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/peer_info.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/debug.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/config.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/kademlia/dht_tracker.hpp" +#include "libtorrent/session_status.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/file_pool.hpp" +#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/natpmp.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/lsd.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/connection_queue.hpp" +#include "libtorrent/disk_io_thread.hpp" + +namespace libtorrent +{ + + namespace fs = boost::filesystem; + + namespace aux + { + struct session_impl; + + // this data is shared between the main thread and the + // thread that initialize pieces + struct piece_checker_data + { + piece_checker_data() + : processing(false), progress(0.f), abort(false) {} + + boost::shared_ptr torrent_ptr; + fs::path save_path; + + sha1_hash info_hash; + + void parse_resume_data( + const entry& rd + , const torrent_info& info + , std::string& error); + + std::vector piece_map; + std::vector unfinished_pieces; + std::vector block_info; + std::vector peers; + entry resume_data; + + // this is true if this torrent is being processed (checked) + // if it is not being processed, then it can be removed from + // the queue without problems, otherwise the abort flag has + // to be set. + bool processing; + + // is filled in by storage::initialize_pieces() + // and represents the progress. It should be a + // value in the range [0, 1] + float progress; + + // abort defaults to false and is typically + // filled in by torrent_handle when the user + // aborts the torrent + bool abort; + }; + + struct checker_impl: boost::noncopyable + { + checker_impl(session_impl& s): m_ses(s), m_abort(false) {} + void operator()(); + piece_checker_data* find_torrent(const sha1_hash& info_hash); + void remove_torrent(sha1_hash const& info_hash); + +#ifndef NDEBUG + void check_invariant() const; +#endif + + // when the files has been checked + // the torrent is added to the session + session_impl& m_ses; + + mutable boost::mutex m_mutex; + boost::condition m_cond; + + // a list of all torrents that are currently in queue + // or checking their files + std::deque > m_torrents; + std::deque > m_processing; + + bool m_abort; + }; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + struct tracker_logger; +#endif + + // this is the link between the main thread and the + // thread started to run the main downloader loop + struct session_impl: boost::noncopyable + { +#ifndef NDEBUG + friend class ::libtorrent::peer_connection; +#endif + friend struct checker_impl; + friend class invariant_access; + typedef std::map + , boost::intrusive_ptr > + connection_map; + typedef std::map > torrent_map; + + session_impl( + std::pair listen_port_range + , fingerprint const& cl_fprint + , char const* listen_interface = "0.0.0.0"); + ~session_impl(); + +#ifndef TORRENT_DISABLE_EXTENSIONS + void add_extension(boost::function(torrent*)> ext); +#endif + void operator()(); + + void open_listen_port(); + + void async_accept(); + void on_incoming_connection(boost::shared_ptr const& s + , boost::weak_ptr const& as, asio::error_code const& e); + + // must be locked to access the data + // in this struct + typedef boost::recursive_mutex mutex_t; + mutable mutex_t m_mutex; + + boost::weak_ptr find_torrent(const sha1_hash& info_hash); + peer_id const& get_peer_id() const { return m_peer_id; } + + void close_connection(boost::intrusive_ptr const& p); + void connection_failed(boost::shared_ptr const& s + , tcp::endpoint const& a, char const* message); + + void set_settings(session_settings const& s); + session_settings const& settings() const { return m_settings; } + +#ifndef TORRENT_DISABLE_DHT + void add_dht_node(std::pair const& node); + void add_dht_node(udp::endpoint n); + void add_dht_router(std::pair const& node); + void set_dht_settings(dht_settings const& s); + dht_settings const& get_dht_settings() const { return m_dht_settings; } + void start_dht(entry const& startup_state); + void stop_dht(); + entry dht_state() const; +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + void set_pe_settings(pe_settings const& settings); + pe_settings const& get_pe_settings() const { return m_pe_settings; } +#endif + + // called when a port mapping is successful, or a router returns + // a failure to map a port + void on_port_mapping(int tcp_port, int udp_port, std::string const& errmsg); + + bool is_aborted() const { return m_abort; } + + void set_ip_filter(ip_filter const& f); + void set_port_filter(port_filter const& f); + + bool listen_on( + std::pair const& port_range + , const char* net_interface = 0); + bool is_listening() const; + + torrent_handle add_torrent( + torrent_info const& ti + , fs::path const& save_path + , entry const& resume_data + , bool compact_mode + , int block_size + , storage_constructor_type sc); + + 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 + , bool compact_mode + , int block_size + , storage_constructor_type sc); + + void remove_torrent(torrent_handle const& h); + + std::vector get_torrents(); + + void set_severity_level(alert::severity_t s); + std::auto_ptr pop_alert(); + + int upload_rate_limit() const; + int download_rate_limit() const; + + void set_download_rate_limit(int bytes_per_second); + void set_upload_rate_limit(int bytes_per_second); + void set_max_half_open_connections(int limit); + void set_max_connections(int limit); + void set_max_uploads(int limit); + + int max_connections() const { return m_max_connections; } + int max_uploads() const { return m_max_uploads; } + + int num_uploads() const { return m_num_unchoked; } + int num_connections() const + { return m_connections.size(); } + + void unchoke_peer(peer_connection& c) + { + c.send_unchoke(); + ++m_num_unchoked; + } + + session_status status() const; + void set_peer_id(peer_id const& id); + void set_key(int key); + unsigned short listen_port() const; + + void abort(); + + torrent_handle find_torrent_handle(sha1_hash const& info_hash); + + void announce_lsd(sha1_hash const& ih); + + void set_peer_proxy(proxy_settings const& s) + { m_peer_proxy = s; } + void set_web_seed_proxy(proxy_settings const& s) + { m_web_seed_proxy = s; } + void set_tracker_proxy(proxy_settings const& s) + { m_tracker_proxy = s; } + + proxy_settings const& peer_proxy() const + { return m_peer_proxy; } + proxy_settings const& web_seed_proxy() const + { return m_web_seed_proxy; } + proxy_settings const& tracker_proxy() const + { return m_tracker_proxy; } + +#ifndef TORRENT_DISABLE_DHT + void set_dht_proxy(proxy_settings const& s) + { m_dht_proxy = s; } + proxy_settings const& dht_proxy() const + { return m_dht_proxy; } +#endif + + void start_lsd(); + void start_natpmp(); + void start_upnp(); + + void stop_lsd(); + void stop_natpmp(); + void stop_upnp(); + + // handles delayed alerts + alert_manager m_alerts; + +// private: + + void on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih); + + // this is where all active sockets are stored. + // the selector can sleep while there's no activity on + // them + io_service m_io_service; + asio::strand m_strand; + + // the file pool that all storages in this session's + // torrents uses. It sets a limit on the number of + // open files by this session. + // file pool must be destructed after the torrents + // since they will still have references to it + // when they are destructed. + file_pool m_files; + + // handles disk io requests asynchronously + disk_io_thread m_disk_thread; + + // this is a list of half-open tcp connections + // (only outgoing connections) + // this has to be one of the last + // members to be destructed + connection_queue m_half_open; + + // the bandwidth manager is responsible for + // handing out bandwidth to connections that + // asks for it, it can also throttle the + // rate. + bandwidth_manager m_download_channel; + bandwidth_manager m_upload_channel; + + bandwidth_manager* m_bandwidth_manager[2]; + + tracker_manager m_tracker_manager; + torrent_map m_torrents; + + // this maps sockets to their peer_connection + // object. It is the complete list of all connected + // peers. + connection_map m_connections; + + // filters incoming connections + ip_filter m_ip_filter; + + // filters outgoing connections + port_filter m_port_filter; + + // the peer id that is generated at the start of the session + peer_id m_peer_id; + + // the key is an id that is used to identify the + // client with the tracker only. It is randomized + // at startup + int m_key; + + // the range of ports we try to listen on + std::pair m_listen_port_range; + + // the ip-address of the interface + // we are supposed to listen on. + // if the ip is set to zero, it means + // that we should let the os decide which + // interface to listen on + tcp::endpoint m_listen_interface; + + // this is typically set to the same as the local + // listen port. In case a NAT port forward was + // successfully opened, this will be set to the + // port that is open on the external (NAT) interface + // on the NAT box itself. This is the port that has + // to be published to peers, since this is the port + // the client is reachable through. + int m_external_listen_port; + + boost::shared_ptr m_listen_socket; + + // the settings for the client + session_settings m_settings; + // the proxy settings for different + // kinds of connections + proxy_settings m_peer_proxy; + proxy_settings m_web_seed_proxy; + proxy_settings m_tracker_proxy; +#ifndef TORRENT_DISABLE_DHT + proxy_settings m_dht_proxy; +#endif + + // set to true when the session object + // is being destructed and the thread + // should exit + volatile bool m_abort; + + int m_max_uploads; + int m_max_connections; + + // the number of unchoked peers + int m_num_unchoked; + + // this is initialized to the unchoke_interval + // session_setting and decreased every second. + // when it reaches zero, it is reset to the + // unchoke_interval and the unchoke set is + // recomputed. + int m_unchoke_time_scaler; + + // works like unchoke_time_scaler but it + // is only decresed when the unchoke set + // is recomputed, and when it reaches zero, + // the optimistic unchoke is moved to another peer. + int m_optimistic_unchoke_time_scaler; + + // works like unchoke_time_scaler. Each time + // it reaches 0, and all the connections are + // used, the worst connection will be disconnected + // from the torrent with the most peers + int m_disconnect_time_scaler; + + // statistics gathered from all torrents. + stat m_stat; + + // is false by default and set to true when + // the first incoming connection is established + // this is used to know if the client is behind + // NAT or not. + bool m_incoming_connection; + + void second_tick(asio::error_code const& e); + ptime m_last_tick; + +#ifndef TORRENT_DISABLE_DHT + boost::intrusive_ptr m_dht; + dht_settings m_dht_settings; + // if this is set to true, the dht listen port + // will be set to the same as the tcp listen port + // and will be synchronlized with it as it changes + // it defaults to true + bool m_dht_same_port; + + // see m_external_listen_port. This is the same + // but for the udp port used by the DHT. + int m_external_udp_port; +#endif + +#ifndef TORRENT_DISABLE_ENCRYPTION + pe_settings m_pe_settings; +#endif + + boost::shared_ptr m_natpmp; + boost::shared_ptr m_upnp; + boost::shared_ptr m_lsd; + + // the timer used to fire the second_tick + deadline_timer m_timer; + + // the index of the torrent that will be offered to + // connect to a peer next time second_tick is called. + // This implements a round robin. + int m_next_connect_torrent; +#ifndef NDEBUG + void check_invariant(const char *place = 0); +#endif + +#ifdef TORRENT_STATS + // logger used to write bandwidth usage statistics + std::ofstream m_stats_logger; + int m_second_counter; +#endif +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + boost::shared_ptr create_log(std::string const& name + , int instance, bool append = true); + + // this list of tracker loggers serves as tracker_callbacks when + // shutting down. This list is just here to keep them alive during + // whe shutting down process + std::list > m_tracker_loggers; + + public: + boost::shared_ptr m_logger; + private: +#endif + +#ifndef TORRENT_DISABLE_EXTENSIONS + typedef std::list(torrent*)> > extension_list_t; + + extension_list_t m_extensions; +#endif + + // data shared between the main thread + // and the checker thread + checker_impl m_checker_impl; + + // the main working thread + boost::scoped_ptr m_thread; + + // the thread that calls initialize_pieces() + // on all torrents before they start downloading + boost::scoped_ptr m_checker_thread; + }; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + struct tracker_logger : request_callback + { + tracker_logger(session_impl& ses): m_ses(ses) {} + void tracker_warning(std::string const& str) + { + debug_log("*** tracker warning: " + str); + } + + void tracker_response(tracker_request const& + , std::vector& peers + , int interval + , int complete + , int incomplete) + { + std::stringstream s; + s << "TRACKER RESPONSE:\n" + "interval: " << interval << "\n" + "peers:\n"; + for (std::vector::const_iterator i = peers.begin(); + i != peers.end(); ++i) + { + s << " " << std::setfill(' ') << std::setw(16) << i->ip + << " " << std::setw(5) << std::dec << i->port << " "; + if (!i->pid.is_all_zeros()) s << " " << i->pid; + s << "\n"; + } + debug_log(s.str()); + } + + void tracker_request_timed_out( + tracker_request const&) + { + debug_log("*** tracker timed out"); + } + + void tracker_request_error( + tracker_request const& + , int response_code + , const std::string& str) + { + debug_log(std::string("*** tracker error: ") + + boost::lexical_cast(response_code) + ": " + + str); + } + + void debug_log(const std::string& line) + { + (*m_ses.m_logger) << line << "\n"; + } + session_impl& m_ses; + }; +#endif + + } +} + + +#endif + diff --git a/libtorrent/include/libtorrent/session_settings.hpp b/libtorrent/include/libtorrent/session_settings.hpp index 3a145c687..e5bb9879a 100644 --- a/libtorrent/include/libtorrent/session_settings.hpp +++ b/libtorrent/include/libtorrent/session_settings.hpp @@ -105,8 +105,7 @@ namespace libtorrent , send_redundant_have(false) , lazy_bitfields(true) , inactivity_timeout(600) - , unchoke_interval(15) - , optimistic_unchoke_multiplier(4) + , unchoke_interval(20) , num_want(200) , initial_picker_threshold(4) , allowed_fast_set_size(10) @@ -243,10 +242,6 @@ namespace libtorrent // the number of seconds between chokes/unchokes int unchoke_interval; - // the number of unchoke intervals between - // optimistic unchokes - int optimistic_unchoke_multiplier; - // if this is set, this IP will be reported do the // tracker in the ip= parameter. address announce_ip; diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index 78bc53ab4..2eef2656b 100755 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -62,6 +62,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/tracker_manager.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/alert.hpp" +#include "libtorrent/resource_request.hpp" #include "libtorrent/piece_picker.hpp" #include "libtorrent/config.hpp" #include "libtorrent/escape_string.hpp" @@ -153,6 +154,10 @@ namespace libtorrent bool verify_resume_data(entry& rd, std::string& error) { assert(m_storage); return m_storage->verify_resume_data(rd, error); } + // is called every second by session. This will + // caclulate the upload/download and number + // of connections this torrent needs. And prepare + // it for being used by allocate_resources. void second_tick(stat& accumulator, float tick_interval); // debug purpose only @@ -511,6 +516,11 @@ namespace libtorrent // -------------------------------------------- // RESOURCE MANAGEMENT + void distribute_resources(float tick_interval); + + resource_request m_uploads_quota; + resource_request m_connections_quota; + void set_peer_upload_limit(tcp::endpoint ip, int limit); void set_peer_download_limit(tcp::endpoint ip, int limit); @@ -520,9 +530,7 @@ namespace libtorrent int download_limit() const; void set_max_uploads(int limit); - int max_uploads() const { return m_max_uploads; } void set_max_connections(int limit); - int max_connections() const { return m_max_connections; } void move_storage(fs::path const& save_path); // unless this returns true, new connections must wait @@ -697,9 +705,9 @@ namespace libtorrent // determine the timeout until next try. int m_failed_trackers; - // this is a counter that is decreased every - // second, and when it reaches 0, the policy::pulse() - // is called and the time scaler is reset to 10. + // this is a counter that is increased every + // second, and when it reaches 10, the policy::pulse() + // is called and the time scaler is reset to 0. int m_time_scaler; // the bitmask that says which pieces we have @@ -766,12 +774,6 @@ namespace libtorrent session_settings const& m_settings; storage_constructor_type m_storage_constructor; - - // the maximum number of uploads for this torrent - int m_max_uploads; - - // the maximum number of connections for this torrent - int m_max_connections; #ifndef TORRENT_DISABLE_EXTENSIONS typedef std::list > extension_list_t; diff --git a/libtorrent/include/libtorrent/web_peer_connection.hpp b/libtorrent/include/libtorrent/web_peer_connection.hpp index 1290f14a1..b3ca73c4d 100755 --- a/libtorrent/include/libtorrent/web_peer_connection.hpp +++ b/libtorrent/include/libtorrent/web_peer_connection.hpp @@ -65,6 +65,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert.hpp" #include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent.hpp" +#include "libtorrent/allocate_resources.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/config.hpp" diff --git a/libtorrent/src/allocate_resources.cpp b/libtorrent/src/allocate_resources.cpp new file mode 100644 index 000000000..deef06dc4 --- /dev/null +++ b/libtorrent/src/allocate_resources.cpp @@ -0,0 +1,225 @@ +/* + +Copyright (c) 2006, Magnus Jonsson, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +//The Standard Library defines the two template functions std::min() +//and std::max() in the header. In general, you should +//use these template functions for calculating the min and max values +//of a pair. Unfortunately, Visual C++ does not define these function +// templates. This is because the names min and max clash with +//the traditional min and max macros defined in . +//As a workaround, Visual C++ defines two alternative templates with +//identical functionality called _cpp_min() and _cpp_max(). You can +//use them instead of std::min() and std::max().To disable the +//generation of the min and max macros in Visual C++, #define +//NOMINMAX before #including . + +#include "libtorrent/pch.hpp" + +#ifdef _WIN32 + //support boost1.32.0(2004-11-19 18:47) + //now all libs can be compiled and linked with static module + #define NOMINMAX +#endif + +#include "libtorrent/allocate_resources.hpp" +#include "libtorrent/size_type.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/aux_/allocate_resources_impl.hpp" + +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1310 +#define for if (false) {} else for +#else +#include +#endif + +namespace libtorrent +{ + int saturated_add(int a, int b) + { + assert(a >= 0); + assert(b >= 0); + assert(a <= resource_request::inf); + assert(b <= resource_request::inf); + assert(resource_request::inf + resource_request::inf < 0); + + unsigned int sum = unsigned(a) + unsigned(b); + if (sum > unsigned(resource_request::inf)) + sum = resource_request::inf; + + assert(sum >= unsigned(a) && sum >= unsigned(b)); + return int(sum); + } + +#if defined(_MSC_VER) && _MSC_VER < 1310 + + namespace detail + { + struct iterator_wrapper + { + typedef std::map >::iterator orig_iter; + + orig_iter iter; + + iterator_wrapper(orig_iter i): iter(i) {} + void operator++() { ++iter; } + torrent& operator*() { return *(iter->second); } + bool operator==(const iterator_wrapper& i) const + { return iter == i.iter; } + bool operator!=(const iterator_wrapper& i) const + { return iter != i.iter; } + }; + + struct iterator_wrapper2 + { + typedef std::map::iterator orig_iter; + + orig_iter iter; + + iterator_wrapper2(orig_iter i): iter(i) {} + void operator++() { ++iter; } + peer_connection& operator*() { return *(iter->second); } + bool operator==(const iterator_wrapper2& i) const + { return iter == i.iter; } + bool operator!=(const iterator_wrapper2& i) const + { return iter != i.iter; } + }; + + } + + void allocate_resources( + int resources + , std::map >& c + , resource_request torrent::* res) + { + aux::allocate_resources_impl( + resources + , detail::iterator_wrapper(c.begin()) + , detail::iterator_wrapper(c.end()) + , res); + } + + void allocate_resources( + int resources + , std::map& c + , resource_request peer_connection::* res) + { + aux::allocate_resources_impl( + resources + , detail::iterator_wrapper2(c.begin()) + , detail::iterator_wrapper2(c.end()) + , res); + } + +#else + + namespace aux + { + peer_connection& pick_peer( + std::pair + , boost::intrusive_ptr > const& p) + { + return *p.second; + } + + peer_connection& pick_peer2( + std::pair const& p) + { + return *p.second; + } + + torrent& deref(std::pair > const& p) + { + return *p.second; + } + + session& deref(session* p) + { + return *p; + } + } + + void allocate_resources( + int resources + , std::map >& c + , resource_request torrent::* res) + { + typedef std::map >::iterator orig_iter; + typedef std::pair > in_param; + typedef boost::transform_iterator new_iter; + + aux::allocate_resources_impl( + resources + , new_iter(c.begin(), &aux::deref) + , new_iter(c.end(), &aux::deref) + , res); + } + + void allocate_resources( + int resources + , std::map& c + , resource_request peer_connection::* res) + { + typedef std::map::iterator orig_iter; + typedef std::pair in_param; + typedef boost::transform_iterator new_iter; + + aux::allocate_resources_impl( + resources + , new_iter(c.begin(), &aux::pick_peer2) + , new_iter(c.end(), &aux::pick_peer2) + , res); + } + + void allocate_resources( + int resources + , std::vector& _sessions + , resource_request session::* res) + { + typedef std::vector::iterator orig_iter; + typedef session* in_param; + typedef boost::transform_iterator new_iter; + + aux::allocate_resources_impl( + resources + , new_iter(_sessions.begin(), &aux::deref) + , new_iter(_sessions.end(), &aux::deref) + , res); + } + +#endif + +} // namespace libtorrent diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index 2be76e5dd..86d4e1500 100755 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -109,8 +109,8 @@ namespace libtorrent , m_prefer_whole_pieces(false) , m_request_large_blocks(false) , m_non_prioritized(false) - , m_upload_limit(bandwidth_limit::inf) - , m_download_limit(bandwidth_limit::inf) + , m_upload_limit(resource_request::inf) + , m_download_limit(resource_request::inf) , m_peer_info(peerinfo) , m_speed(slow) , m_connection_ticket(-1) @@ -185,8 +185,8 @@ namespace libtorrent , m_prefer_whole_pieces(false) , m_request_large_blocks(false) , m_non_prioritized(false) - , m_upload_limit(bandwidth_limit::inf) - , m_download_limit(bandwidth_limit::inf) + , m_upload_limit(resource_request::inf) + , m_download_limit(resource_request::inf) , m_peer_info(peerinfo) , m_speed(slow) , m_remote_bytes_dled(0) @@ -526,18 +526,30 @@ namespace libtorrent && p.start + p.length <= t->torrent_file().piece_size(p.piece) && (p.start % t->block_size() == 0); } - + + struct disconnect_torrent + { + disconnect_torrent(boost::weak_ptr& t): m_t(&t) {} + ~disconnect_torrent() { if (m_t) m_t->reset(); } + void cancel() { m_t = 0; } + private: + boost::weak_ptr* m_t; + }; + void peer_connection::attach_to_torrent(sha1_hash const& ih) { INVARIANT_CHECK; assert(!m_disconnecting); - assert(m_torrent.expired()); - boost::weak_ptr wpt = m_ses.find_torrent(ih); + m_torrent = m_ses.find_torrent(ih); + boost::shared_ptr t = m_torrent.lock(); if (t && t->is_aborted()) + { + m_torrent.reset(); t.reset(); + } if (!t) { @@ -548,6 +560,7 @@ namespace libtorrent throw std::runtime_error("got info-hash that is not in our session"); } + disconnect_torrent disconnect(m_torrent); if (t->is_paused()) { // paused torrents will not accept @@ -558,27 +571,21 @@ namespace libtorrent throw std::runtime_error("connection rejected by paused torrent"); } - assert(m_torrent.expired()); // check to make sure we don't have another connection with the same // info_hash and peer_id. If we do. close this connection. t->attach_peer(this); - m_torrent = wpt; - - assert(!m_torrent.expired()); // if the torrent isn't ready to accept // connections yet, we'll have to wait with // our initialization if (t->ready_for_connections()) init(); - assert(!m_torrent.expired()); - // assume the other end has no pieces // if we don't have valid metadata yet, // leave the vector unallocated assert(m_num_pieces == 0); std::fill(m_have_piece.begin(), m_have_piece.end(), false); - assert(!m_torrent.expired()); + disconnect.cancel(); } // message handlers @@ -1536,7 +1543,7 @@ namespace libtorrent // if the peer has the piece and we want // to download it, request it - if (int(m_have_piece.size()) > index + if (m_have_piece.size() > index && m_have_piece[index] && t->has_picker() && t->picker().piece_priority(index) > 0) @@ -1893,7 +1900,7 @@ namespace libtorrent void peer_connection::set_upload_limit(int limit) { assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); + if (limit == -1) limit = resource_request::inf; if (limit < 10) limit = 10; m_upload_limit = limit; m_bandwidth_limit[upload_channel].throttle(m_upload_limit); @@ -1902,7 +1909,7 @@ namespace libtorrent void peer_connection::set_download_limit(int limit) { assert(limit >= -1); - if (limit == -1) limit = std::numeric_limits::max(); + if (limit == -1) limit = resource_request::inf; if (limit < 10) limit = 10; m_download_limit = limit; m_bandwidth_limit[download_channel].throttle(m_download_limit); @@ -2037,13 +2044,10 @@ namespace libtorrent if (m_packet_size >= m_recv_pos) m_recv_buffer.resize(m_packet_size); } - void peer_connection::second_tick(float tick_interval) throw() + void peer_connection::second_tick(float tick_interval) { INVARIANT_CHECK; - try - { - ptime now(time_now()); boost::shared_ptr t = m_torrent.lock(); @@ -2182,14 +2186,43 @@ namespace libtorrent } fill_send_buffer(); - } - catch (std::exception& e) +/* + size_type diff = share_diff(); + + enum { block_limit = 2 }; // how many blocks difference is considered unfair + + // if the peer has been choked, send the current piece + // as fast as possible + if (diff > block_limit*m_torrent->block_size() || m_torrent->is_seed() || is_choked()) { -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << "**ERROR**: " << e.what() << "\n"; -#endif - m_ses.connection_failed(m_socket, remote(), e.what()); + // if we have downloaded more than one piece more + // than we have uploaded OR if we are a seed + // have an unlimited upload rate + m_ul_bandwidth_quota.wanted = std::numeric_limits::max(); } + else + { + float ratio = m_torrent->ratio(); + // if we have downloaded too much, response with an + // upload rate of 10 kB/s more than we dowlload + // if we have uploaded too much, send with a rate of + // 10 kB/s less than we receive + int bias = 0; + if (diff > -block_limit*m_torrent->block_size()) + { + bias = static_cast(m_statistics.download_rate() * ratio) / 2; + if (bias < 10*1024) bias = 10*1024; + } + else + { + bias = -static_cast(m_statistics.download_rate() * ratio) / 2; + } + m_ul_bandwidth_quota.wanted = static_cast(m_statistics.download_rate()) + bias; + + // the maximum send_quota given our download rate from this peer + if (m_ul_bandwidth_quota.wanted < 256) m_ul_bandwidth_quota.wanted = 256; + } +*/ } void peer_connection::fill_send_buffer() diff --git a/libtorrent/src/policy.cpp b/libtorrent/src/policy.cpp index 129f10a3b..e5d625dff 100755 --- a/libtorrent/src/policy.cpp +++ b/libtorrent/src/policy.cpp @@ -189,8 +189,7 @@ namespace libtorrent // infinite loop, fighting to request the same blocks. void request_a_block(torrent& t, peer_connection& c) { - if (t.is_seed()) return; - + assert(!t.is_seed()); assert(t.valid_metadata()); assert(c.peer_info_struct() != 0 || !dynamic_cast(&c)); int num_requests = c.desired_queue_size() @@ -332,8 +331,9 @@ namespace libtorrent policy::policy(torrent* t) : m_torrent(t) + , m_num_unchoked(0) , m_available_free_upload(0) -// , m_last_optimistic_disconnect(min_time()) + , m_last_optimistic_disconnect(min_time()) { assert(t); } // disconnects and removes all peers that are now filtered @@ -375,7 +375,7 @@ namespace libtorrent m_peers.erase(i++); } } -/* + // finds the peer that has the worst download rate // and returns it. May return 0 if all peers are // choked. @@ -457,7 +457,7 @@ namespace libtorrent } return unchoke_peer; } -*/ + policy::iterator policy::find_disconnect_candidate() { INVARIANT_CHECK; @@ -542,7 +542,7 @@ namespace libtorrent return candidate; } -/* + policy::iterator policy::find_seed_choke_candidate() { INVARIANT_CHECK; @@ -648,7 +648,7 @@ namespace libtorrent --m_num_unchoked; } } -*/ + void policy::pulse() { INVARIANT_CHECK; @@ -680,7 +680,7 @@ 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; @@ -692,9 +692,10 @@ namespace libtorrent ++num_connected_peers; } - if (m_torrent->max_connections() != std::numeric_limits::max()) + if (m_torrent->m_connections_quota.given != std::numeric_limits::max()) { - int max_connections = m_torrent->max_connections(); + + int max_connections = m_torrent->m_connections_quota.given; if (num_connected_peers >= max_connections) { @@ -722,7 +723,7 @@ namespace libtorrent --num_connected_peers; } } -*/ + // ------------------------ // upload shift // ------------------------ @@ -753,7 +754,7 @@ namespace libtorrent , m_torrent->end() , m_available_free_upload); } -/* + // ------------------------ // seed choking policy // ------------------------ @@ -869,7 +870,6 @@ namespace libtorrent while (m_num_unchoked < m_torrent->m_uploads_quota.given && unchoke_one_peer()); } -*/ } int policy::count_choked() const @@ -902,8 +902,7 @@ namespace libtorrent // override at a time assert(c.remote() == c.get_socket()->remote_endpoint()); - if (m_torrent->num_peers() >= m_torrent->max_connections() - && m_torrent->session().num_connections() >= m_torrent->session().max_connections() + if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given && c.remote().address() != m_torrent->current_tracker().address()) { throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect @@ -985,7 +984,7 @@ namespace libtorrent i->connection = &c; assert(i->connection); i->connected = time_now(); -// m_last_optimistic_disconnect = time_now(); + m_last_optimistic_disconnect = time_now(); } void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid @@ -1173,38 +1172,14 @@ namespace libtorrent // In that case we don't care if people are leeching, they // can't pay for their downloads anyway. if (c.is_choked() - && m_torrent->session().num_uploads() < m_torrent->session().max_uploads() + && m_num_unchoked < m_torrent->m_uploads_quota.given && (m_torrent->ratio() == 0 || c.share_diff() >= -free_upload_amount || m_torrent->is_seed())) { - m_torrent->session().unchoke_peer(c); + c.send_unchoke(); + ++m_num_unchoked; } -#if defined(TORRENT_VERBOSE_LOGGING) - else if (c.is_choked()) - { - std::string reason; - if (m_torrent->session().num_uploads() >= m_torrent->session().max_uploads()) - { - reason = "the number of uploads (" - + boost::lexical_cast(m_torrent->session().num_uploads()) - + ") is more than or equal to the limit (" - + boost::lexical_cast(m_torrent->session().max_uploads()) - + ")"; - } - else - { - reason = "the share ratio (" - + boost::lexical_cast(c.share_diff()) - + ") is <= free_upload_amount (" - + boost::lexical_cast(int(free_upload_amount)) - + ") and we are not seeding and the ratio (" - + boost::lexical_cast(m_torrent->ratio()) - + ")is non-zero"; - } - (*c.m_logger) << time_now_string() << " DID NOT UNCHOKE [ " << reason << " ]\n"; - } -#endif } // called when a peer is no longer interested in us @@ -1236,7 +1211,7 @@ namespace libtorrent } */ } -/* + bool policy::unchoke_one_peer() { INVARIANT_CHECK; @@ -1265,7 +1240,7 @@ namespace libtorrent p->connection->send_choke(); --m_num_unchoked; } -*/ + bool policy::connect_one_peer() { INVARIANT_CHECK; @@ -1281,7 +1256,7 @@ namespace libtorrent try { - p->connected = time_now(); + p->connected = m_last_optimistic_disconnect = time_now(); p->connection = m_torrent->connect_to_peer(&*p); if (p->connection == 0) return false; p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload); @@ -1321,7 +1296,6 @@ namespace libtorrent // assert(c.is_disconnecting()); bool unchoked = false; -#warning extract policy::peer pointer from c iterator i = std::find_if( m_peers.begin() , m_peers.end() @@ -1331,7 +1305,6 @@ namespace libtorrent if (i == m_peers.end()) return; assert(i->connection == &c); i->connection = 0; - i->optimistically_unchoked = false; i->connected = time_now(); if (!c.is_choked() && !m_torrent->is_aborted()) @@ -1357,15 +1330,15 @@ namespace libtorrent i->prev_amount_download += c.statistics().total_payload_download(); i->prev_amount_upload += c.statistics().total_payload_upload(); -// if (unchoked) -// { + if (unchoked) + { // if the peer that is diconnecting is unchoked // then unchoke another peer in order to maintain // the total number of unchoked peers -// --m_num_unchoked; -// if (m_torrent->is_seed()) seed_unchoke_one_peer(); -// else unchoke_one_peer(); -// } + --m_num_unchoked; + if (m_torrent->is_seed()) seed_unchoke_one_peer(); + else unchoke_one_peer(); + } } catch (std::exception& e) { @@ -1403,6 +1376,7 @@ namespace libtorrent void policy::check_invariant() const { if (m_torrent->is_aborted()) return; + int actual_unchoked = 0; int connected_peers = 0; int total_connections = 0; @@ -1431,7 +1405,10 @@ namespace libtorrent ++nonempty_connections; if (!p.connection->is_disconnecting()) ++connected_peers; + if (!p.connection->is_choked()) ++actual_unchoked; } +// assert(actual_unchoked <= m_torrent->m_uploads_quota.given); + assert(actual_unchoked == m_num_unchoked); int num_torrent_peers = 0; for (torrent::const_peer_iterator i = m_torrent->begin(); @@ -1498,7 +1475,6 @@ namespace libtorrent , failcount(0) , hashfails(0) , seed(false) - , optimistically_unchoked(false) , last_optimistically_unchoked(min_time()) , connected(min_time()) , trust_points(0) diff --git a/libtorrent/src/session.cpp b/libtorrent/src/session.cpp index 6d6fa55b2..485c90d62 100755 --- a/libtorrent/src/session.cpp +++ b/libtorrent/src/session.cpp @@ -68,6 +68,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert_types.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/file.hpp" +#include "libtorrent/allocate_resources.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/socket.hpp" diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index 694e5f49d..81f48a3ce 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -68,6 +68,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert_types.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/file.hpp" +#include "libtorrent/allocate_resources.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/ip_filter.hpp" #include "libtorrent/socket.hpp" @@ -222,12 +223,6 @@ namespace detail if (!m_ses.is_aborted()) { m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr)); - if (m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(torrent_checked_alert( - processing->torrent_ptr->get_handle() - , "torrent finished checking")); - } if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info)) { m_ses.m_alerts.post_alert(torrent_finished_alert( @@ -350,12 +345,6 @@ namespace detail processing->torrent_ptr->files_checked(processing->unfinished_pieces); m_ses.m_torrents.insert(std::make_pair( processing->info_hash, processing->torrent_ptr)); - if (m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(torrent_checked_alert( - processing->torrent_ptr->get_handle() - , "torrent finished checking")); - } if (processing->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info)) { @@ -516,12 +505,8 @@ namespace detail , m_listen_interface(address::from_string(listen_interface), listen_port_range.first) , m_external_listen_port(0) , m_abort(false) - , m_max_uploads(8) - , m_max_connections(200) - , m_num_unchoked(0) - , m_unchoke_time_scaler(0) - , m_optimistic_unchoke_time_scaler(0) - , m_disconnect_time_scaler(0) + , m_max_uploads(-1) + , m_max_connections(-1) , m_incoming_connection(false) , m_last_tick(time_now()) #ifndef TORRENT_DISABLE_DHT @@ -836,10 +821,7 @@ namespace detail assert(p->is_disconnecting()); connection_map::iterator i = m_connections.find(p->get_socket()); if (i != m_connections.end()) - { - if (!i->second->is_choked()) --m_num_unchoked; m_connections.erase(i); - } } void session_impl::set_peer_id(peer_id const& id) @@ -919,9 +901,7 @@ namespace detail // round robin fashion, so that every torrent is // equallt likely to connect to a peer - if (!m_torrents.empty() - && m_half_open.free_slots() - && num_connections() < m_max_connections) + if (!m_torrents.empty() && m_half_open.free_slots()) { // this is the maximum number of connections we will // attempt this tick @@ -938,13 +918,11 @@ namespace detail { torrent& t = *i->second; if (t.want_more_peers()) - { if (t.try_connect_peer()) { --max_connections; steps_since_last_connect = 0; } - } ++m_next_connect_torrent; ++steps_since_last_connect; ++i; @@ -998,7 +976,18 @@ namespace detail continue; } - c.keep_alive(); + try + { + c.keep_alive(); + } + catch (std::exception& exc) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*c.m_logger) << "**ERROR**: " << exc.what() << "\n"; +#endif + c.set_failed(); + c.disconnect(); + } } // check each torrent for tracker updates @@ -1030,148 +1019,30 @@ namespace detail } m_stat.second_tick(tick_interval); + // distribute the maximum upload rate among the torrents - // -------------------------------------------------------------- - // unchoke set and optimistic unchoke calculations - // -------------------------------------------------------------- - m_unchoke_time_scaler--; - if (m_unchoke_time_scaler <= 0 && !m_connections.empty()) + assert(m_max_uploads >= -1); + assert(m_max_connections >= -1); + + allocate_resources(m_max_uploads == -1 + ? std::numeric_limits::max() + : m_max_uploads + , m_torrents + , &torrent::m_uploads_quota); + + allocate_resources(m_max_connections == -1 + ? std::numeric_limits::max() + : m_max_connections + , m_torrents + , &torrent::m_connections_quota); + + for (std::map >::iterator i + = m_torrents.begin(); i != m_torrents.end(); ++i) { - 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->second.get(); - torrent* t = p->associated_torrent().lock().get(); - if (!p->peer_info_struct() - || !p->is_peer_interested() - || p->is_disconnecting() - || p->is_connecting() - || (p->share_diff() < -free_upload_amount - && t && !t->is_seed())) - { - if (!i->second->is_choked()) - i->second->send_choke(); - continue; - } - peers.push_back(i->second.get()); - } - - // sort 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(&stat::total_payload_upload, bind(&peer_connection::statistics, _1)) - < bind(&stat::total_payload_upload, bind(&peer_connection::statistics, _2))); - - std::stable_sort(peers.begin(), peers.end() - , bind(&stat::download_payload_rate, bind(&peer_connection::statistics, _1)) - > bind(&stat::download_payload_rate, bind(&peer_connection::statistics, _2))); - - // reserve one upload slot for optimistic unchokes - int unchoke_set_size = m_max_uploads - 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; - assert(p); - if (unchoke_set_size > 0) - { - if (p->is_choked()) p->send_unchoke(); - 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; - } - --unchoke_set_size; - ++m_num_unchoked; - } - else - { - if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked) - p->send_choke(); - } - } - - 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->second.get(); - assert(p); - policy::peer* pi = p->peer_info_struct(); - if (!pi) continue; - if (pi->optimistically_unchoked) - { - 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()) - { - 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()) - { - current_optimistic_unchoke->second->send_choke(); - current_optimistic_unchoke->second->peer_info_struct()->optimistically_unchoked = false; - } - - optimistic_unchoke_candidate->second->send_unchoke(); - optimistic_unchoke_candidate->second->peer_info_struct()->optimistically_unchoked = true; - } - - if (optimistic_unchoke_candidate != m_connections.end()) - ++m_num_unchoked; - } - } - - // -------------------------------------------------------------- - // disconnect peers when we have too many - // -------------------------------------------------------------- - --m_disconnect_time_scaler; - if (m_disconnect_time_scaler <= 0) - { - m_disconnect_time_scaler = 60; - - // every 60 seconds, disconnect the worst peer - // if we have reached the connection limit - if (num_connections() >= max_connections() && !m_torrents.empty()) - { - torrent_map::iterator i = std::max_element(m_torrents.begin(), m_torrents.end() - , bind(&torrent::num_peers, bind(&torrent_map::value_type::second, _1))); - - assert(i != m_torrents.end()); - i->second->get_policy().disconnect_one_peer(); - } +#ifndef NDEBUG + i->second->check_invariant(); +#endif + i->second->distribute_resources(tick_interval); } } catch (std::exception& exc) @@ -1995,6 +1866,24 @@ namespace detail m_bandwidth_manager[peer_connection::upload_channel]->throttle(bytes_per_second); } + int session_impl::num_uploads() const + { + int uploads = 0; + mutex_t::scoped_lock l(m_mutex); + for (torrent_map::const_iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; i++) + { + uploads += i->second->get_policy().num_uploads(); + } + return uploads; + } + + int session_impl::num_connections() const + { + mutex_t::scoped_lock l(m_mutex); + return m_connections.size(); + } + std::auto_ptr session_impl::pop_alert() { mutex_t::scoped_lock l(m_mutex); @@ -2089,25 +1978,17 @@ namespace detail void session_impl::check_invariant(const char *place) { assert(place); - int unchokes = 0; - int num_optimistic = 0; for (connection_map::iterator i = m_connections.begin(); i != m_connections.end(); ++i) { assert(i->second); boost::shared_ptr t = i->second->associated_torrent().lock(); - if (!i->second->is_choked()) ++unchokes; - if (i->second->peer_info_struct() - && i->second->peer_info_struct()->optimistically_unchoked) - ++num_optimistic; if (t) { assert(t->get_policy().has_connection(boost::get_pointer(i->second))); } } - assert(num_optimistic == 0 || num_optimistic == 1); - assert(m_num_unchoked == unchokes); } #endif diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index f26563503..13309c1e7 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -199,12 +199,18 @@ namespace libtorrent , m_connections_initialized(true) , m_settings(s) , m_storage_constructor(sc) - , m_max_uploads(std::numeric_limits::max()) - , m_max_connections(std::numeric_limits::max()) { #ifndef NDEBUG m_initial_done = 0; #endif + + m_uploads_quota.min = 0; + m_connections_quota.min = 2; + // this will be corrected the next time the main session + // distributes resources, i.e. on average in 0.5 seconds + m_connections_quota.given = 100; + m_uploads_quota.max = std::numeric_limits::max(); + m_connections_quota.max = std::numeric_limits::max(); m_policy.reset(new policy(this)); } @@ -271,6 +277,13 @@ namespace libtorrent if (name) m_name.reset(new std::string(name)); + m_uploads_quota.min = 0; + m_connections_quota.min = 2; + // this will be corrected the next time the main session + // distributes resources, i.e. on average in 0.5 seconds + m_connections_quota.given = 100; + m_uploads_quota.max = std::numeric_limits::max(); + m_connections_quota.max = std::numeric_limits::max(); if (tracker_url) { m_trackers.push_back(announce_entry(tracker_url)); @@ -316,14 +329,6 @@ namespace libtorrent INVARIANT_CHECK; -#if defined(TORRENT_VERBOSE_LOGGING) - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - (*i->second->m_logger) << "*** DESTRUCTING TORRENT\n"; - } -#endif - assert(m_abort); if (!m_connections.empty()) disconnect_all(); @@ -1015,15 +1020,6 @@ namespace libtorrent m_event = tracker_request::stopped; // disconnect all peers and close all // files belonging to the torrents - -#if defined(TORRENT_VERBOSE_LOGGING) - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - (*i->second->m_logger) << "*** ABORTING TORRENT\n"; - } -#endif - disconnect_all(); if (m_owning_storage.get()) m_storage->async_release_files(); m_owning_storage = 0; @@ -1894,7 +1890,7 @@ namespace libtorrent void torrent::attach_peer(peer_connection* p) { -// INVARIANT_CHECK; + INVARIANT_CHECK; assert(p != 0); assert(!p->is_local()); @@ -1959,7 +1955,7 @@ namespace libtorrent bool torrent::want_more_peers() const { - return int(m_connections.size()) < m_max_connections + return int(m_connections.size()) < m_connections_quota.given && m_ses.m_half_open.free_slots() && !m_paused; } @@ -2429,15 +2425,15 @@ namespace libtorrent void torrent::set_max_uploads(int limit) { assert(limit >= -1); - if (limit < 0) limit = std::numeric_limits::max(); - m_max_uploads = limit; + if (limit == -1) limit = std::numeric_limits::max(); + m_uploads_quota.max = std::max(m_uploads_quota.min, limit); } void torrent::set_max_connections(int limit) { assert(limit >= -1); - if (limit < -1) limit = std::numeric_limits::max(); - m_max_connections = limit; + if (limit == -1) limit = std::numeric_limits::max(); + m_connections_quota.max = std::max(m_connections_quota.min, limit); } void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit) @@ -2500,14 +2496,6 @@ namespace libtorrent } #endif -#if defined(TORRENT_VERBOSE_LOGGING) - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - (*i->second->m_logger) << "*** PAUSING TORRENT\n"; - } -#endif - disconnect_all(); m_paused = true; // tell the tracker that we stopped @@ -2540,6 +2528,10 @@ namespace libtorrent #endif m_paused = false; + m_uploads_quota.min = 0; + m_connections_quota.min = 2; + m_uploads_quota.max = std::numeric_limits::max(); + m_connections_quota.max = std::numeric_limits::max(); // tell the tracker that we're back m_event = tracker_request::started; @@ -2553,6 +2545,10 @@ namespace libtorrent { INVARIANT_CHECK; + m_connections_quota.used = (int)m_connections.size(); + m_uploads_quota.used = m_policy->num_uploads(); + m_uploads_quota.max = (int)m_connections.size(); + #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) @@ -2565,6 +2561,10 @@ namespace libtorrent { // let the stats fade out to 0 m_stat.second_tick(tick_interval); + m_connections_quota.min = 0; + m_connections_quota.max = 0; + m_uploads_quota.min = 0; + m_uploads_quota.max = 0; return; } @@ -2623,13 +2623,6 @@ namespace libtorrent } accumulator += m_stat; m_stat.second_tick(tick_interval); - - m_time_scaler--; - if (m_time_scaler <= 0) - { - m_time_scaler = 10; - m_policy->pulse(); - } } bool torrent::try_connect_peer() @@ -2638,6 +2631,18 @@ namespace libtorrent return m_policy->connect_one_peer(); } + void torrent::distribute_resources(float tick_interval) + { + INVARIANT_CHECK; + + m_time_scaler--; + if (m_time_scaler <= 0) + { + m_time_scaler = settings().unchoke_interval; + m_policy->pulse(); + } + } + void torrent::async_verify_piece(int piece_index, boost::function const& f) { INVARIANT_CHECK; @@ -2759,10 +2764,10 @@ namespace libtorrent = m_trackers[m_last_working_tracker].url; } - st.num_uploads = -1; - st.uploads_limit = m_max_uploads; - st.num_connections = int(m_connections.size()); - st.connections_limit = m_max_connections; + st.num_uploads = m_uploads_quota.used; + st.uploads_limit = m_uploads_quota.given; + st.num_connections = m_connections_quota.used; + st.connections_limit = m_connections_quota.given; // if we don't have any metadata, stop here if (!valid_metadata())