diff --git a/ChangeLog b/ChangeLog index 7ba3eccf6..d155b87a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * Improved the bandwidth limiter (it now implements a leaky bucket). * Improved the HTTP seed downloader to report accurate progress. * Added more client peer-id signatures to be recognized. * added support for HTTP servers that skip the CR before the NL at line breaks. diff --git a/Jamfile b/Jamfile index e45ee9093..6fc8be072 100755 --- a/Jamfile +++ b/Jamfile @@ -32,6 +32,7 @@ feature.compose logging : TORRENT_DHT_VERBOSE_LOGGING ; SOURCES = allocate_resources.cpp alert.cpp + bandwidth_manager.cpp entry.cpp escape_string.cpp file.cpp diff --git a/examples/client_test.cpp b/examples/client_test.cpp index ac8071561..188be15ad 100755 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -229,18 +229,15 @@ std::string progress_bar(float progress, int width, char const* code = "33") return std::string(bar.begin(), bar.end()); } -char const* peer_index(libtorrent::tcp::endpoint addr, std::vector const& peers) +int peer_index(libtorrent::tcp::endpoint addr, std::vector const& peers) { using namespace libtorrent; std::vector::const_iterator i = std::find_if(peers.begin() , peers.end(), boost::bind(std::equal_to() , bind(&peer_info::ip, _1), addr)); - if (i == peers.end()) return "+"; + if (i == peers.end()) return -1; - static char str[] = " "; - int index = i - peers.begin(); - str[0] = (index < 10)?'0' + index:'A' + index - 10; - return str; + return i - peers.begin(); } void print_peer_info(std::ostream& out, std::vector const& peers) @@ -909,14 +906,21 @@ int main(int ac, char* av[]) out << i->piece_index << ": ["; for (int j = 0; j < i->blocks_in_piece; ++j) { - char const* peer_str = peer_index(i->peer[j], peers); + int index = peer_index(i->peer[j], peers); + static char str[] = "+"; + if (index >= 0) + str[0] = (index < 10)?'0' + index:'A' + index - 10; + #ifdef ANSI_TERMINAL_COLORS - if (i->finished_blocks[j]) out << esc("32;7") << peer_str << esc("0"); - else if (i->requested_blocks[j]) out << peer_str; + if (peers[index].downloading_piece_index == i->piece_index + && peers[index].downloading_block_index == j) + out << esc("33;7") << str << esc("0"); + else if (i->finished_blocks[j]) out << esc("32;7") << str << esc("0"); + else if (i->requested_blocks[j]) out << str; else out << "-"; #else if (i->finished_blocks[j]) out << "#"; - else if (i->requested_blocks[j]) out << peer_str; + else if (i->requested_blocks[j]) out << str; else out << "-"; #endif } diff --git a/include/Makefile.am b/include/Makefile.am index 70d98fb00..fdbdbf683 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,6 +1,7 @@ nobase_include_HEADERS = libtorrent/alert.hpp \ libtorrent/alert_types.hpp \ libtorrent/allocate_resources.hpp \ +libtorrent/bandwidth_manager.hpp \ libtorrent/bencode.hpp \ libtorrent/buffer.hpp \ libtorrent/config.hpp \ diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 3051f8d7e..130b60747 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -76,6 +76,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/session.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/file_pool.hpp" +#include "libtorrent/bandwidth_manager.hpp" namespace libtorrent { @@ -282,6 +283,13 @@ namespace libtorrent io_service m_io_service; asio::strand m_strand; + // the bandwidth manager is responsible for + // handing out bandwidth to connections that + // asks for it, it can also throttle the + // rate. + bandwidth_manager m_dl_bandwidth_manager; + bandwidth_manager m_ul_bandwidth_manager; + tracker_manager m_tracker_manager; torrent_map m_torrents; @@ -331,11 +339,6 @@ namespace libtorrent // should exit volatile bool m_abort; - // maximum upload rate given in - // bytes per second. -1 means - // unlimited - int m_upload_rate; - int m_download_rate; int m_max_uploads; int m_max_connections; // the number of simultaneous half-open tcp diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index 4fc07d107..44c290d53 100755 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -71,6 +71,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/config.hpp" #include "libtorrent/session.hpp" +#include "libtorrent/bandwidth_manager.hpp" // TODO: each time a block is 'taken over' // from another peer. That peer must be given @@ -102,6 +103,13 @@ namespace libtorrent friend void intrusive_ptr_release(peer_connection const*); public: + enum channels + { + upload_channel, + download_channel, + num_channels + }; + // this is the constructor where the we are the active part. // The peer_conenction should handshake and verify that the // other end has the correct id @@ -285,10 +293,13 @@ namespace libtorrent void cancel_request(piece_block const& b); void send_block_requests(); - // how much bandwidth we're using, how much we want, - // and how much we are allowed to use. - resource_request m_ul_bandwidth_quota; - resource_request m_dl_bandwidth_quota; + int max_assignable_bandwidth(int channel) const + { + return m_bandwidth_limit[channel].max_assignable(); + } + + void assign_bandwidth(int channel, int amount); + void expire_bandwidth(int channel, int amount); #ifndef NDEBUG void check_invariant() const; @@ -365,6 +376,10 @@ namespace libtorrent bool verify_piece(peer_request const& p) const; + // the bandwidth channels, upload and download + // keeps track of the current quotas + bandwidth_limit m_bandwidth_limit[num_channels]; + // statistics about upload and download speeds // and total amount of uploads and downloads for // this peer diff --git a/include/libtorrent/piece_picker.hpp b/include/libtorrent/piece_picker.hpp index c3e7a55a2..429ec34f6 100755 --- a/include/libtorrent/piece_picker.hpp +++ b/include/libtorrent/piece_picker.hpp @@ -337,6 +337,9 @@ namespace libtorrent // the required popularity of a piece in order to download // it in sequence instead of random order. int m_sequenced_download_threshold; +#ifndef NDEBUG + bool m_files_checked_called; +#endif }; inline int piece_picker::blocks_in_piece(int index) const diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 3609a5d69..efde693a4 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifdef _MSC_VER #pragma warning(pop) @@ -66,6 +67,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/piece_picker.hpp" #include "libtorrent/config.hpp" #include "libtorrent/escape_string.hpp" +#include "libtorrent/bandwidth_manager.hpp" namespace libtorrent { @@ -188,6 +190,17 @@ namespace libtorrent float ratio() const { return m_ratio; } +// -------------------------------------------- + // BANDWIDTH MANAGEMENT + + bandwidth_limit m_bandwidth_limit[2]; + + void request_bandwidth(int channel + , boost::intrusive_ptr p); + + void expire_bandwidth(int channel, int amount); + void assign_bandwidth(int channel, int amount); + // -------------------------------------------- // PEER MANAGEMENT @@ -413,12 +426,8 @@ namespace libtorrent // -------------------------------------------- // RESOURCE MANAGEMENT - // this will distribute the given upload/download - // quotas and number of connections, among the peers void distribute_resources(float tick_interval); - resource_request m_ul_bandwidth_quota; - resource_request m_dl_bandwidth_quota; resource_request m_uploads_quota; resource_request m_connections_quota; @@ -536,6 +545,9 @@ namespace libtorrent boost::scoped_ptr m_picker; + // the queue of peer_connections that want more bandwidth + std::deque > m_bandwidth_queue[2]; + std::vector m_trackers; // this is an index into m_torrent_file.trackers() int m_last_working_tracker; @@ -585,20 +597,6 @@ namespace libtorrent // are opened through tcp::endpoint m_net_interface; - // the max number of bytes this torrent - // can upload per second - int m_upload_bandwidth_limit; - int m_download_bandwidth_limit; - - // the accumulated excess upload and download - // bandwidth used. Used to balance out the - // bandwidth to match the limit over time - int m_excess_ul; - int m_excess_dl; - - int m_soft_ul_limit; - int m_soft_dl_limit; - boost::filesystem::path m_save_path; // determines the storage state for this torrent. diff --git a/src/Makefile.am b/src/Makefile.am index dd4e7d4b6..d4c4974a0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ lib_LTLIBRARIES = libtorrent.la libtorrent_la_SOURCES = allocate_resources.cpp \ -entry.cpp escape_string.cpp \ +bandwidth_manager.cpp entry.cpp escape_string.cpp \ peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \ storage.cpp torrent.cpp torrent_handle.cpp \ @@ -26,6 +26,7 @@ $(top_srcdir)/include/libtorrent/alert_types.hpp \ $(top_srcdir)/include/libtorrent/allocate_resources.hpp \ $(top_srcdir)/include/libtorrent/aux_/allocate_resources_impl.hpp \ $(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ +$(top_srcdir)/include/libtorrent/bandwidth_manager.hpp \ $(top_srcdir)/include/libtorrent/bencode.hpp \ $(top_srcdir)/include/libtorrent/buffer.hpp \ $(top_srcdir)/include/libtorrent/debug.hpp \ diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index 33eb19827..ab9f9ec8c 100755 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -132,6 +132,14 @@ namespace libtorrent // we have to wait for the handshake to see // which torrent the connector want's to connect to + + // upload bandwidth will only be given to connections + // that are part of a torrent. Since this is an incoming + // connection, we have to give it some initial bandwidth + // to send the handshake. + m_bandwidth_limit[download_channel].assign(80); + m_bandwidth_limit[upload_channel].assign(80); + // start in the state where we are trying to read the // handshake from the other side reset_recv_buffer(1); @@ -178,15 +186,15 @@ namespace libtorrent p.total_download = statistics().total_payload_download(); p.total_upload = statistics().total_payload_upload(); - if (m_ul_bandwidth_quota.given == std::numeric_limits::max()) + if (m_bandwidth_limit[upload_channel].throttle() == bandwidth_limit::inf) p.upload_limit = -1; else - p.upload_limit = m_ul_bandwidth_quota.given; + p.upload_limit = m_bandwidth_limit[upload_channel].throttle(); - if (m_dl_bandwidth_quota.given == std::numeric_limits::max()) + if (m_bandwidth_limit[download_channel].throttle() == bandwidth_limit::inf) p.download_limit = -1; else - p.download_limit = m_dl_bandwidth_quota.given; + p.download_limit = m_bandwidth_limit[download_channel].throttle(); p.load_balancing = total_free_upload(); diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index b35dbeb34..c1487320e 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -131,63 +131,6 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); assert(t); - // these numbers are used the first second of connection. - // then the given upload limits will be applied by running - // allocate_resources(). - m_ul_bandwidth_quota.min = 10; - m_ul_bandwidth_quota.max = resource_request::inf; - - if (t->m_ul_bandwidth_quota.given == resource_request::inf) - { - m_ul_bandwidth_quota.given = resource_request::inf; - } - else - { - if (t->num_peers() == 0) - { - // just enough to get started with the handshake and bitmask - m_ul_bandwidth_quota.given = 400; - } - else - { - // set the limit of this new connection to the mean of the other connections - size_type total_ul_given = 0; - for (torrent::peer_iterator i = t->begin() - , end(t->end()); i != end; ++i) - { - total_ul_given += i->second->m_ul_bandwidth_quota.given; - } - m_ul_bandwidth_quota.given = total_ul_given / t->num_peers(); - } - } - - m_dl_bandwidth_quota.min = 10; - m_dl_bandwidth_quota.max = resource_request::inf; - - if (t->m_dl_bandwidth_quota.given == resource_request::inf) - { - m_dl_bandwidth_quota.given = resource_request::inf; - } - else - { - if (t->num_peers() == 0) - { - // just enough to get started with the handshake and bitmask - m_dl_bandwidth_quota.given = 400; - } - else - { - // set the limit of this new connection to the mean of the other connections - size_type total_dl_given = 0; - for (torrent::peer_iterator i = t->begin() - , end(t->end()); i != end; ++i) - { - total_dl_given += i->second->m_dl_bandwidth_quota.given; - } - m_dl_bandwidth_quota.given = total_dl_given / t->num_peers(); - } - } - std::fill(m_peer_id.begin(), m_peer_id.end(), 0); if (t->ready_for_connections()) @@ -250,24 +193,7 @@ namespace libtorrent + boost::lexical_cast(remote().port()), m_ses.listen_port()); (*m_logger) << "*** INCOMING CONNECTION\n"; #endif - - - // upload bandwidth will only be given to connections - // that are part of a torrent. Since this is an incoming - // connection, we have to give it some initial bandwidth - // to send the handshake. - // after one second, allocate_resources() will be called - // and the correct bandwidth limits will be set on all - // connections. - - m_ul_bandwidth_quota.min = 10; - m_ul_bandwidth_quota.max = resource_request::inf; - m_ul_bandwidth_quota.given = 400; - - m_dl_bandwidth_quota.min = 10; - m_dl_bandwidth_quota.max = resource_request::inf; - m_dl_bandwidth_quota.given = 400; - + std::fill(m_peer_id.begin(), m_peer_id.end(), 0); } @@ -278,7 +204,6 @@ namespace libtorrent } #endif - void peer_connection::init() { INVARIANT_CHECK; @@ -467,16 +392,6 @@ namespace libtorrent m_free_upload += free_upload; } - void peer_connection::reset_upload_quota() - { - m_ul_bandwidth_quota.reset(); - m_dl_bandwidth_quota.reset(); - assert(m_ul_bandwidth_quota.left() >= 0); - assert(m_dl_bandwidth_quota.left() >= 0); - setup_send(); - setup_receive(); - } - // verifies a piece to see if it is valid (is within a valid range) // and if it can correspond to a request generated by libtorrent. bool peer_connection::verify_piece(const peer_request& p) const @@ -1560,6 +1475,7 @@ namespace libtorrent { INVARIANT_CHECK; + assert(packet_size > 0); assert((int)m_recv_buffer.size() >= size); std::copy(m_recv_buffer.begin() + size, m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.begin()); @@ -1658,33 +1574,12 @@ namespace libtorrent // maintain the share ratio given by m_ratio // with all peers. - if (has_peer_choked() || !is_interesting()) - { - // if the peer choked us, we can't download - // any data anyway, limit the max download - // bandwidth. If we're a seed, it doesn't matter, - // we won't be interested in downloading anyway - m_dl_bandwidth_quota.max = 400; - } - else - { - m_dl_bandwidth_quota.max = resource_request::inf; - } - if (t->is_seed() || is_choked() || t->ratio() == 0.0f) { // if we have downloaded more than one piece more // than we have uploaded OR if we are a seed // have an unlimited upload rate - // the exception is if we have choked the peer, and - // we have a limited amount of upload bandwidth, then - // we set the max to 400, just enough for the - // protocol chatter. - if (is_choked() - && m_ul_bandwidth_quota.given < resource_request::inf) - m_ul_bandwidth_quota.max = 400; - else - m_ul_bandwidth_quota.max = resource_request::inf; + m_bandwidth_limit[upload_channel].throttle(bandwidth_limit::inf); } else { @@ -1707,17 +1602,10 @@ namespace libtorrent upload_speed_limit = std::min(upload_speed_limit, (double)std::numeric_limits::max()); - m_ul_bandwidth_quota.max - = std::max((int)upload_speed_limit, m_ul_bandwidth_quota.min); + m_bandwidth_limit[upload_channel].throttle( + std::min(std::max((int)upload_speed_limit, 20) + , m_upload_limit)); } - if (m_ul_bandwidth_quota.given > m_ul_bandwidth_quota.max) - m_ul_bandwidth_quota.given = m_ul_bandwidth_quota.max; - - if (m_upload_limit < m_ul_bandwidth_quota.max) - m_ul_bandwidth_quota.max = m_upload_limit; - - if (m_download_limit < m_dl_bandwidth_quota.max) - m_dl_bandwidth_quota.max = m_download_limit; fill_send_buffer(); /* @@ -1808,6 +1696,38 @@ namespace libtorrent } } + void peer_connection::assign_bandwidth(int channel, int amount) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "bandwidth [ " << channel << " ] + " << amount << "\n"; +#endif + + m_bandwidth_limit[channel].assign(amount); + if (channel == upload_channel) + { + m_writing = false; + setup_send(); + } + else if (channel == download_channel) + { + m_reading = false; + setup_receive(); + } + } + + void peer_connection::expire_bandwidth(int channel, int amount) + { + m_bandwidth_limit[channel].expire(amount); + if (channel == upload_channel) + { + setup_send(); + } + else if (channel == download_channel) + { + setup_receive(); + } + } + void peer_connection::setup_send() { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); @@ -1815,6 +1735,31 @@ namespace libtorrent INVARIANT_CHECK; if (m_writing) return; + + shared_ptr t = m_torrent.lock(); + + if (m_bandwidth_limit[upload_channel].quota_left() == 0 + && (!m_send_buffer[m_current_send_buffer].empty() + || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) + && !m_connecting + && t) + { + // in this case, we have data to send, but no + // bandwidth. So, we simply request bandwidth + // from the torrent + assert(t); + if (m_bandwidth_limit[upload_channel].max_assignable() > 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "req bandwidth [ " << upload_channel << " ]\n"; +#endif + + t->request_bandwidth(upload_channel, self()); + m_writing = true; + } + return; + } + if (!can_write()) return; assert(!m_writing); @@ -1832,7 +1777,7 @@ namespace libtorrent if (!m_send_buffer[sending_buffer].empty()) { int amount_to_send - = std::min(m_ul_bandwidth_quota.left() + = std::min(m_bandwidth_limit[upload_channel].quota_left() , (int)m_send_buffer[sending_buffer].size() - m_write_pos); assert(amount_to_send > 0); @@ -1853,23 +1798,41 @@ namespace libtorrent INVARIANT_CHECK; if (m_reading) return; + + shared_ptr t = m_torrent.lock(); + + if (m_bandwidth_limit[download_channel].quota_left() == 0 + && !m_connecting + && t) + { + assert(t); + if (m_bandwidth_limit[download_channel].max_assignable() > 0) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "req bandwidth [ " << download_channel << " ]\n"; +#endif + t->request_bandwidth(download_channel, self()); + m_reading = true; + } + return; + } + if (!can_read()) return; assert(m_packet_size > 0); int max_receive = std::min( - m_dl_bandwidth_quota.left() + m_bandwidth_limit[download_channel].quota_left() , m_packet_size - m_recv_pos); + assert(max_receive > 0); assert(m_recv_pos >= 0); assert(m_packet_size > 0); - assert(m_dl_bandwidth_quota.left() > 0); assert(max_receive > 0); assert(can_read()); m_socket->async_read_some(asio::buffer(&m_recv_buffer[m_recv_pos] , max_receive), bind(&peer_connection::on_receive_data, self(), _1, _2)); m_reading = true; - assert(m_dl_bandwidth_quota.used <= m_dl_bandwidth_quota.given); } void peer_connection::reset_recv_buffer(int packet_size) @@ -1925,7 +1888,7 @@ namespace libtorrent assert(m_reading); m_reading = false; // correct the dl quota usage, if not all of the buffer was actually read - m_dl_bandwidth_quota.used += bytes_transferred; + m_bandwidth_limit[download_channel].use_quota(bytes_transferred); if (error) { @@ -2004,7 +1967,7 @@ namespace libtorrent // we want to send data return (!m_send_buffer[m_current_send_buffer].empty() || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) - && m_ul_bandwidth_quota.left() > 0 + && m_bandwidth_limit[upload_channel].quota_left() > 0 && !m_connecting; } @@ -2012,7 +1975,8 @@ namespace libtorrent { INVARIANT_CHECK; - return m_dl_bandwidth_quota.left() > 0 && !m_connecting; + return m_bandwidth_limit[download_channel].quota_left() > 0 + && !m_connecting; } void peer_connection::connect() @@ -2069,6 +2033,7 @@ namespace libtorrent m_connecting = false; on_connected(); setup_send(); + setup_receive(); } catch (std::exception& ex) { @@ -2097,8 +2062,8 @@ namespace libtorrent assert(m_writing); m_writing = false; - // correct the ul quota usage, if not all of the buffer was sent - m_ul_bandwidth_quota.used += bytes_transferred; + + m_bandwidth_limit[upload_channel].use_quota(bytes_transferred); m_write_pos += bytes_transferred; if (error) diff --git a/src/piece_picker.cpp b/src/piece_picker.cpp index 6e2b0271d..a018e42ef 100755 --- a/src/piece_picker.cpp +++ b/src/piece_picker.cpp @@ -62,6 +62,9 @@ namespace libtorrent { assert(blocks_per_piece > 0); assert(total_num_blocks >= 0); +#ifndef NDEBUG + m_files_checked_called = false; +#endif // the piece index is stored in 20 bits, which limits the allowed // number of pieces somewhat @@ -87,6 +90,9 @@ namespace libtorrent const std::vector& pieces , const std::vector& unfinished) { +#ifndef NDEBUG + m_files_checked_called = true; +#endif // build a vector of all the pieces we don't have std::vector piece_list; piece_list.reserve(std::count(pieces.begin(), pieces.end(), false)); @@ -423,6 +429,7 @@ namespace libtorrent assert(elem_index != piece_pos::we_have_index); std::vector >& src_vec(pick_piece_info_vector( downloading, filtered)); + assert(m_files_checked_called); assert((int)src_vec.size() > priority); assert((int)src_vec[priority].size() > elem_index); @@ -537,6 +544,7 @@ namespace libtorrent assert(!filtered); assert(priority >= 0); assert(elem_index >= 0); + assert(m_files_checked_called); std::vector >& src_vec(pick_piece_info_vector(downloading, filtered)); @@ -588,6 +596,7 @@ namespace libtorrent assert(index >= 0); assert(index < (int)m_piece_map.size()); + assert(m_files_checked_called); assert(m_piece_map[index].downloading == 1); @@ -609,6 +618,7 @@ namespace libtorrent TORRENT_PIECE_PICKER_INVARIANT_CHECK; assert(i >= 0); assert(i < (int)m_piece_map.size()); + assert(m_files_checked_called); int index = m_piece_map[i].index; int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold); @@ -636,6 +646,7 @@ namespace libtorrent { TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(m_files_checked_called); assert(i >= 0); assert(i < (int)m_piece_map.size()); @@ -758,6 +769,7 @@ namespace libtorrent TORRENT_PIECE_PICKER_INVARIANT_CHECK; assert(num_blocks > 0); assert(pieces.size() == m_piece_map.size()); + assert(m_files_checked_called); // free refers to pieces that are free to download, no one else // is downloading them. diff --git a/src/session_impl.cpp b/src/session_impl.cpp index e46f00e48..5dce58a82 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -476,12 +476,12 @@ namespace libtorrent { namespace detail , fingerprint const& cl_fprint , char const* listen_interface) : m_strand(m_io_service) + , m_dl_bandwidth_manager(m_io_service, peer_connection::download_channel) + , m_ul_bandwidth_manager(m_io_service, peer_connection::upload_channel) , m_tracker_manager(m_settings) , m_listen_port_range(listen_port_range) , m_listen_interface(address::from_string(listen_interface), listen_port_range.first) , m_abort(false) - , m_upload_rate(-1) - , m_download_rate(-1) , m_max_uploads(-1) , m_max_connections(-1) , m_half_open_limit(-1) @@ -505,6 +505,8 @@ namespace libtorrent { namespace detail "3. hard download quota\n" "\n"; m_second_counter = 0; + m_dl_bandwidth_manager.m_ses = this; + m_ul_bandwidth_manager.m_ses = this; #endif // ---- generate a peer id ---- @@ -560,7 +562,7 @@ namespace libtorrent { namespace detail assert(!m_abort); // abort the main thread m_abort = true; - m_io_service.interrupt(); + m_io_service.stop(); l.unlock(); mutex::scoped_lock l2(m_checker_impl.m_mutex); @@ -895,7 +897,7 @@ namespace libtorrent { namespace detail (*m_logger) << "*** SECOND TIMER FAILED " << e.message() << "\n"; #endif m_abort = true; - m_io_service.interrupt(); + m_io_service.stop(); return; } @@ -976,29 +978,9 @@ namespace libtorrent { namespace detail // distribute the maximum upload rate among the torrents - assert(m_upload_rate >= -1); - assert(m_download_rate >= -1); assert(m_max_uploads >= -1); assert(m_max_connections >= -1); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_stats_logger) << m_second_counter++ << "\t" - << (m_upload_rate == -1 ? m_upload_rate - : int(m_upload_rate * tick_interval)) << "\n"; -#endif - - allocate_resources(m_upload_rate == -1 - ? std::numeric_limits::max() - : int(m_upload_rate * tick_interval) - , m_torrents - , &torrent::m_ul_bandwidth_quota); - - allocate_resources(m_download_rate == -1 - ? std::numeric_limits::max() - : int(m_download_rate * tick_interval) - , m_torrents - , &torrent::m_dl_bandwidth_quota); - allocate_resources(m_max_uploads == -1 ? std::numeric_limits::max() : m_max_uploads @@ -1115,7 +1097,7 @@ namespace libtorrent { namespace detail { tracker_timer.expires_from_now(boost::posix_time::milliseconds(100)); tracker_timer.async_wait(m_strand.wrap( - bind(&io_service::interrupt, &m_io_service))); + bind(&io_service::stop, &m_io_service))); m_io_service.reset(); m_io_service.run(); @@ -1570,8 +1552,10 @@ namespace libtorrent { namespace detail { assert(bytes_per_second > 0 || bytes_per_second == -1); mutex_t::scoped_lock l(m_mutex); - m_download_rate = bytes_per_second; + if (bytes_per_second == -1) bytes_per_second = bandwidth_limit::inf; + m_dl_bandwidth_manager.throttle(bytes_per_second); } + bool session_impl::is_listening() const { mutex_t::scoped_lock l(m_mutex); @@ -1584,7 +1568,7 @@ namespace libtorrent { namespace detail // lock the main thread and abort it mutex_t::scoped_lock l(m_mutex); m_abort = true; - m_io_service.interrupt(); + m_io_service.stop(); } m_thread->join(); @@ -1640,7 +1624,8 @@ namespace libtorrent { namespace detail { assert(bytes_per_second > 0 || bytes_per_second == -1); mutex_t::scoped_lock l(m_mutex); - m_upload_rate = bytes_per_second; + if (bytes_per_second == -1) bytes_per_second = bandwidth_limit::inf; + m_ul_bandwidth_manager.throttle(bytes_per_second); } int session_impl::num_uploads() const @@ -1679,13 +1664,13 @@ namespace libtorrent { namespace detail int session_impl::upload_rate_limit() const { mutex_t::scoped_lock l(m_mutex); - return m_upload_rate; + return m_ul_bandwidth_manager.throttle(); } int session_impl::download_rate_limit() const { mutex_t::scoped_lock l(m_mutex); - return m_download_rate; + return m_dl_bandwidth_manager.throttle(); } #ifndef NDEBUG @@ -1711,9 +1696,6 @@ namespace libtorrent { namespace detail error_log << "peer_connection::is_connecting() " << p->is_connecting() << "\n"; error_log << "peer_connection::can_write() " << p->can_write() << "\n"; error_log << "peer_connection::can_read() " << p->can_read() << "\n"; - error_log << "peer_connection::ul_quota_left " << p->m_ul_bandwidth_quota.left() << "\n"; - error_log << "peer_connection::dl_quota_left " << p->m_dl_bandwidth_quota.left() << "\n"; - error_log << "peer_connection::m_ul_bandwidth_quota.given " << p->m_ul_bandwidth_quota.given << "\n"; error_log << "peer_connection::get_peer_id " << p->pid() << "\n"; error_log << "place: " << place << "\n"; error_log.flush(); diff --git a/src/torrent.cpp b/src/torrent.cpp index 7210d074b..0ae5ff38e 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -258,12 +258,6 @@ namespace libtorrent , m_total_failed_bytes(0) , m_total_redundant_bytes(0) , m_net_interface(net_interface.address(), 0) - , m_upload_bandwidth_limit(std::numeric_limits::max()) - , m_download_bandwidth_limit(std::numeric_limits::max()) - , m_excess_ul(0) - , m_excess_dl(0) - , m_soft_ul_limit(10000) - , m_soft_dl_limit(10000) , m_save_path(complete(save_path)) , m_compact_mode(compact_mode) , m_default_block_size(block_size) @@ -294,33 +288,6 @@ namespace libtorrent // 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_dl_bandwidth_quota.min = 100; - m_dl_bandwidth_quota.max = resource_request::inf; - - if (m_ses.m_download_rate == -1) - { - m_dl_bandwidth_quota.given = resource_request::inf; - } - else - { - m_dl_bandwidth_quota.given = 400; - } - - m_ul_bandwidth_quota.min = 100; - m_ul_bandwidth_quota.max = resource_request::inf; - - if (m_ses.m_upload_rate == -1) - { - m_ul_bandwidth_quota.given = resource_request::inf; - } - else - { - m_ul_bandwidth_quota.given = 400; - } - m_policy.reset(new policy(this)); init(); @@ -375,12 +342,6 @@ namespace libtorrent , m_total_failed_bytes(0) , m_total_redundant_bytes(0) , m_net_interface(net_interface.address(), 0) - , m_upload_bandwidth_limit(std::numeric_limits::max()) - , m_download_bandwidth_limit(std::numeric_limits::max()) - , m_excess_ul(0) - , m_excess_dl(0) - , m_soft_ul_limit(10000) - , m_soft_dl_limit(10000) , m_save_path(complete(save_path)) , m_compact_mode(compact_mode) , m_default_block_size(block_size) @@ -412,32 +373,6 @@ namespace libtorrent m_connections_quota.given = 100; m_uploads_quota.max = std::numeric_limits::max(); m_connections_quota.max = std::numeric_limits::max(); - - m_dl_bandwidth_quota.min = 100; - m_dl_bandwidth_quota.max = resource_request::inf; - - if (m_ses.m_download_rate == -1) - { - m_dl_bandwidth_quota.given = resource_request::inf; - } - else - { - m_dl_bandwidth_quota.given = 400; - } - - m_ul_bandwidth_quota.min = 100; - m_ul_bandwidth_quota.max = resource_request::inf; - - - if (m_ses.m_upload_rate == -1) - { - m_ul_bandwidth_quota.given = resource_request::inf; - } - else - { - m_ul_bandwidth_quota.given = 400; - } - m_trackers.push_back(announce_entry(tracker_url)); m_policy.reset(new policy(this)); @@ -1297,12 +1232,6 @@ namespace libtorrent boost::shared_ptr s(new stream_socket(m_ses.m_io_service)); boost::intrusive_ptr c(new web_peer_connection( m_ses, shared_from_this(), s, a, url)); - -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << "add url seed (" << c->m_dl_bandwidth_quota.given << ", " - << c->m_dl_bandwidth_quota.used << ") "; - (*m_ses.m_logger) << "\n"; -#endif #ifndef NDEBUG c->m_in_constructor = false; @@ -1359,13 +1288,6 @@ namespace libtorrent boost::intrusive_ptr c(new bt_peer_connection( m_ses, shared_from_this(), s, a)); -#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << "connect_to_peer (" << c->m_dl_bandwidth_quota.given << ", " - << c->m_dl_bandwidth_quota.used << ") "; - (*m_ses.m_logger) << "\n"; -#endif - - #ifndef NDEBUG c->m_in_constructor = false; #endif @@ -1530,6 +1452,47 @@ namespace libtorrent } } + void torrent::request_bandwidth(int channel + , boost::intrusive_ptr p) + { + if (m_bandwidth_limit[channel].max_assignable() >= 17000) + { + if (channel == peer_connection::upload_channel) + m_ses.m_ul_bandwidth_manager.request_bandwidth(p); + else if (channel == peer_connection::download_channel) + m_ses.m_dl_bandwidth_manager.request_bandwidth(p); + m_bandwidth_limit[channel].assign(17000); + } + else + { + m_bandwidth_queue[channel].push_back(p); + } + } + + void torrent::expire_bandwidth(int channel, int amount) + { + assert(amount >= -1); + if (amount == -1) amount = 17000; + m_bandwidth_limit[channel].expire(amount); + + while (!m_bandwidth_queue[channel].empty() && m_bandwidth_limit[channel].max_assignable() >= 17000) + { + intrusive_ptr p = m_bandwidth_queue[channel].front(); + m_bandwidth_queue[channel].pop_front(); + if (channel == peer_connection::upload_channel) + m_ses.m_ul_bandwidth_manager.request_bandwidth(p); + else if (channel == peer_connection::download_channel) + m_ses.m_dl_bandwidth_manager.request_bandwidth(p); + m_bandwidth_limit[channel].assign(17000); + } + } + + void torrent::assign_bandwidth(int channel, int amount) + { + assert(amount >= 0); + if (amount < 17000) expire_bandwidth(channel, 17000 - amount); + } + // called when torrent is finished (all interested pieces downloaded) void torrent::finished() { @@ -1871,7 +1834,7 @@ namespace libtorrent assert(limit >= -1); if (limit == -1) limit = std::numeric_limits::max(); if (limit < num_peers() * 10) limit = num_peers() * 10; - m_upload_bandwidth_limit = limit; + m_bandwidth_limit[peer_connection::upload_channel].throttle(limit); } void torrent::set_download_limit(int limit) @@ -1879,7 +1842,7 @@ namespace libtorrent assert(limit >= -1); if (limit == -1) limit = std::numeric_limits::max(); if (limit < num_peers() * 10) limit = num_peers() * 10; - m_download_bandwidth_limit = limit; + m_bandwidth_limit[peer_connection::download_channel].throttle(limit); } void torrent::pause() @@ -1937,14 +1900,6 @@ namespace libtorrent m_connections_quota.used = (int)m_connections.size(); m_uploads_quota.used = m_policy->num_uploads(); - m_ul_bandwidth_quota.used = 0; - m_ul_bandwidth_quota.max = 0; - m_ul_bandwidth_quota.min = 0; - - m_dl_bandwidth_quota.used = 0; - m_dl_bandwidth_quota.min = 0; - m_dl_bandwidth_quota.max = 0; - #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) @@ -2005,33 +1960,7 @@ namespace libtorrent // updates the peer connection's ul/dl bandwidth // resource requests p->second_tick(tick_interval); - - m_ul_bandwidth_quota.used += p->m_ul_bandwidth_quota.used; - m_ul_bandwidth_quota.min += p->m_ul_bandwidth_quota.min; - m_dl_bandwidth_quota.used += p->m_dl_bandwidth_quota.used; - m_dl_bandwidth_quota.min += p->m_dl_bandwidth_quota.min; - - m_ul_bandwidth_quota.max = saturated_add( - m_ul_bandwidth_quota.max - , p->m_ul_bandwidth_quota.max); - - m_dl_bandwidth_quota.max = saturated_add( - m_dl_bandwidth_quota.max - , p->m_dl_bandwidth_quota.max); } - - m_ul_bandwidth_quota.max - = std::min(m_ul_bandwidth_quota.max, m_upload_bandwidth_limit); - - if (m_upload_bandwidth_limit == resource_request::inf) - m_ul_bandwidth_quota.max = resource_request::inf; - - m_dl_bandwidth_quota.max - = std::min(m_dl_bandwidth_quota.max, m_download_bandwidth_limit); - - if (m_download_bandwidth_limit == resource_request::inf) - m_dl_bandwidth_quota.max = resource_request::inf; - accumulator += m_stat; m_stat.second_tick(tick_interval); m_web_stat.second_tick(tick_interval); @@ -2047,135 +1976,6 @@ namespace libtorrent m_time_scaler = 10; m_policy->pulse(); } - - assert(m_ul_bandwidth_quota.given >= 0); - assert(m_dl_bandwidth_quota.given >= 0); - - int ul_used = 0; - int dl_used = 0; -#ifdef TORRENT_LOGGING - int ul_max = 0; - int dl_max = 0; -#endif - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - peer_connection* p = i->second; - - // the bandwidth exceeding the given amount is accumulated to - // the next timeslice, don't take it into account now as well! - // (that would lead to a spiral of accumulating used-values) - ul_used += std::min(p->m_ul_bandwidth_quota.used, p->m_ul_bandwidth_quota.given); - dl_used += std::min(p->m_dl_bandwidth_quota.used, p->m_dl_bandwidth_quota.given); -#ifdef TORRENT_LOGGING - ul_max = saturated_add(ul_max, p->m_ul_bandwidth_quota.max); - dl_max = saturated_add(dl_max, p->m_dl_bandwidth_quota.max); -#endif - } - - - m_excess_ul += ul_used - m_ul_bandwidth_quota.given; - m_excess_dl += dl_used - m_dl_bandwidth_quota.given; - - m_excess_ul = std::max(m_excess_ul, -10000); - m_excess_dl = std::max(m_excess_dl, -10000); - - int ul_to_distribute = std::max(int((m_ul_bandwidth_quota.given - - m_excess_ul * 0.7f) * 1.6f), 0); - int dl_to_distribute = std::max(int((m_dl_bandwidth_quota.given - - m_excess_dl * 0.7f) * 1.6f), 0); - - m_soft_ul_limit = int(m_soft_ul_limit + (ul_to_distribute - m_soft_ul_limit) * 0.1f); - m_soft_dl_limit = int(m_soft_dl_limit + (dl_to_distribute - m_soft_dl_limit) * 0.1f); - - ul_to_distribute = m_soft_ul_limit; - dl_to_distribute = m_soft_dl_limit; - -#ifdef TORRENT_LOGGING - std::copy(m_ul_history + 1, m_ul_history + debug_bw_history_size, m_ul_history); - m_ul_history[debug_bw_history_size-1] = ul_used; - std::copy(m_dl_history + 1, m_dl_history + debug_bw_history_size, m_dl_history); - m_dl_history[debug_bw_history_size-1] = dl_used; - - size_type mean_ul = 0; - size_type mean_dl = 0; - for (int i = 0; i < debug_bw_history_size; ++i) - { - mean_ul += m_ul_history[i]; - mean_dl += m_dl_history[i]; - } - mean_ul /= debug_bw_history_size; - mean_dl /= debug_bw_history_size; - - int ul_leftovers = 0; - int dl_leftovers = 0; - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - ul_leftovers += i->second->m_ul_bandwidth_quota.leftovers; - dl_leftovers += i->second->m_dl_bandwidth_quota.leftovers; - } - - (*m_log) - << ul_used << "\t" - << mean_ul << "\t" - << dl_used << "\t" - << mean_dl << "\t" - << m_stat.total_payload_download() << "\t" - << m_web_stat.total_payload_download() << "\t" - << m_total_redundant_bytes - << "\n"; - - (*m_log) - << m_second_count++ << "\t" - << m_ul_bandwidth_quota.given << "\t" - << ul_to_distribute << "\t" - << m_excess_ul << "\t" - << ul_leftovers << "\t" - << m_dl_bandwidth_quota.given << "\t" - << dl_to_distribute << "\t" - << m_excess_dl << "\t" - << dl_leftovers << "\t" - << num_peers() << "\t" - << ul_max << "\t" - << dl_max << "\t"; - - (*m_peer_log) << m_second_count << "\t"; - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) - { - int given = i->second->m_dl_bandwidth_quota.given; - (*m_peer_log) << (given == resource_request::inf ? -1 : given) - << "\t" << i->second->m_dl_bandwidth_quota.used << "\t"; - } - for (int i = m_connections.size(); i < 10; ++i) - { - (*m_peer_log) << 0 << "\t" << 0 << "\t"; - } - (*m_peer_log) << "\n"; - -#endif - - // distribute allowed upload among the peers - allocate_resources(ul_to_distribute - , m_connections - , &peer_connection::m_ul_bandwidth_quota); - - // distribute allowed download among the peers - allocate_resources(dl_to_distribute - , m_connections - , &peer_connection::m_dl_bandwidth_quota); - - using boost::bind; - - // tell all peers to reset their used quota. This is - // a new second and they can again use up their quota - - for (std::map::iterator i - = m_connections.begin(); i != m_connections.end(); ++i) - { - i->second->reset_upload_quota(); - } } bool torrent::verify_piece(int piece_index) diff --git a/src/tracker_manager.cpp b/src/tracker_manager.cpp index 4724e764a..64e9c84b5 100755 --- a/src/tracker_manager.cpp +++ b/src/tracker_manager.cpp @@ -46,6 +46,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent.hpp" +#include "libtorrent/peer_connection.hpp" using namespace libtorrent; using boost::tuples::make_tuple; diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index ee1e16782..b0a2548ba 100755 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -442,47 +442,6 @@ namespace libtorrent continue; } break; -/* - if (!m_piece.empty()) - { - // this is not the first partial request we get - if (m_intermediate_piece.start + m_intermediate_piece.length != r.start - || m_intermediate_piece.piece != r.piece) - { - throw std::runtime_error("invalid range in HTTP response"); - } - } - else - { - // this is the first part of a partial request - if (r.start != m_requests.front().start - || r.piece != m_requests.front().piece) - { - throw std::runtime_error("invalid range in HTTP response"); - } - m_intermediate_piece.piece = r.piece; - m_intermediate_piece.start = r.start; - m_intermediate_piece.length = 0; - } - - m_piece.reserve(info.piece_length()); - std::copy(http_body.begin, http_body.end, back_inserter(m_piece)); - m_intermediate_piece.length += r.length; - if (m_intermediate_piece.length == m_requests.front().length) - { - assert(m_requests.front() == m_intermediate_piece); - assert(int(m_piece.size()) == m_intermediate_piece.length); - m_requests.pop_front(); - incoming_piece(m_intermediate_piece, &m_piece[0]); - m_piece.clear(); - } - else if (m_intermediate_piece.length > m_requests.front().length) - { - throw std::runtime_error("too large HTTP response body"); - } - - cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024); -*/ } } @@ -500,15 +459,15 @@ namespace libtorrent p.total_download = statistics().total_payload_download(); p.total_upload = statistics().total_payload_upload(); - if (m_ul_bandwidth_quota.given == std::numeric_limits::max()) + if (m_bandwidth_limit[upload_channel].throttle() == bandwidth_limit::inf) p.upload_limit = -1; else - p.upload_limit = m_ul_bandwidth_quota.given; + p.upload_limit = m_bandwidth_limit[upload_channel].throttle(); - if (m_dl_bandwidth_quota.given == std::numeric_limits::max()) + if (m_bandwidth_limit[download_channel].throttle() == bandwidth_limit::inf) p.download_limit = -1; else - p.download_limit = m_dl_bandwidth_quota.given; + p.download_limit = m_bandwidth_limit[download_channel].throttle(); p.load_balancing = total_free_upload();