diff --git a/CMakeLists.txt b/CMakeLists.txt index ed64c411f..9de16c1f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,9 @@ set(sources alert allocator assert + bandwidth_limit + bandwidth_manager + bandwidth_queue_entry connection_queue create_torrent disk_buffer_holder diff --git a/Jamfile b/Jamfile index c1370349c..efcaccd41 100755 --- a/Jamfile +++ b/Jamfile @@ -342,6 +342,9 @@ SOURCES = alert allocator assert + bandwidth_limit + bandwidth_manager + bandwidth_queue_entry connection_queue create_torrent disk_buffer_holder diff --git a/include/libtorrent/Makefile.am b/include/libtorrent/Makefile.am index c3698f3fb..eec10fb74 100644 --- a/include/libtorrent/Makefile.am +++ b/include/libtorrent/Makefile.am @@ -6,6 +6,7 @@ nobase_include_HEADERS = \ assert.hpp \ bandwidth_limit.hpp \ bandwidth_manager.hpp \ + bandwidth_socket.hpp \ bandwidth_queue_entry.hpp \ bencode.hpp \ bitfield.hpp \ diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 0b425a52c..9a363eddb 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -461,8 +461,8 @@ namespace libtorrent // handing out bandwidth to connections that // asks for it, it can also throttle the // rate. - bandwidth_manager m_download_rate; - bandwidth_manager m_upload_rate; + bandwidth_manager m_download_rate; + bandwidth_manager m_upload_rate; // the global rate limiter bandwidth channels bandwidth_channel m_download_channel; diff --git a/include/libtorrent/bandwidth_limit.hpp b/include/libtorrent/bandwidth_limit.hpp index 0e838e546..0be8df9e0 100644 --- a/include/libtorrent/bandwidth_limit.hpp +++ b/include/libtorrent/bandwidth_limit.hpp @@ -44,58 +44,20 @@ struct bandwidth_channel { static const int inf = boost::integer_traits::const_max; - bandwidth_channel() - : m_quota_left(0) - , m_limit(0) - {} + bandwidth_channel(); // 0 means infinite - void throttle(int limit) - { - TORRENT_ASSERT(limit >= 0); - // if the throttle is more than this, we might overflow - TORRENT_ASSERT(limit < INT_MAX / 31); - m_limit = limit; - } - - int throttle() const - { - return m_limit; - } + void throttle(int limit); + int throttle() const { return m_limit; } - int quota_left() const - { - if (m_limit == 0) return inf; - return (std::max)(m_quota_left, 0); - } - - void update_quota(int dt_milliseconds) - { - if (m_limit == 0) return; - m_quota_left += (m_limit * dt_milliseconds + 500) / 1000; - if (m_quota_left > m_limit * 3) m_quota_left = m_limit * 3; - distribute_quota = (std::max)(m_quota_left, 0); -// fprintf(stderr, "%p: [%d]: + %d limit: %d\n", this, dt_milliseconds, (m_limit * dt_milliseconds + 500) / 1000, m_limit); - } + int quota_left() const; + void update_quota(int dt_milliseconds); // this is used when connections disconnect with // some quota left. It's returned to its bandwidth // channels. - void return_quota(int amount) - { - TORRENT_ASSERT(amount >= 0); - if (m_limit == 0) return; - TORRENT_ASSERT(m_quota_left <= m_quota_left + amount); - m_quota_left += amount; - } - - void use_quota(int amount) - { - TORRENT_ASSERT(amount >= 0); - TORRENT_ASSERT(m_limit >= 0); - if (m_limit == 0) return; - m_quota_left -= amount; - } + void return_quota(int amount); + void use_quota(int amount); // used as temporary storage while distributing // bandwidth diff --git a/include/libtorrent/bandwidth_manager.hpp b/include/libtorrent/bandwidth_manager.hpp index 344669de5..3d0e3598c 100644 --- a/include/libtorrent/bandwidth_manager.hpp +++ b/include/libtorrent/bandwidth_manager.hpp @@ -45,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/assert.hpp" #include "libtorrent/bandwidth_limit.hpp" #include "libtorrent/bandwidth_queue_entry.hpp" +#include "libtorrent/bandwidth_socket.hpp" #include "libtorrent/time.hpp" using boost::intrusive_ptr; @@ -52,197 +53,42 @@ using boost::intrusive_ptr; namespace libtorrent { -template struct bandwidth_manager { bandwidth_manager(int channel #ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT , bool log = false #endif - ) - : m_queued_bytes(0) - , m_channel(channel) - , m_abort(false) - { -#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT - if (log) - m_log.open("bandwidth_limiter.log", std::ios::trunc); - m_start = time_now(); -#endif - } + ); - void close() - { - m_abort = true; - m_queue.clear(); - m_queued_bytes = 0; - error_code ec; - } + void close(); #ifdef TORRENT_DEBUG - bool is_queued(PeerConnection const* peer) const - { - for (typename queue_t::const_iterator i = m_queue.begin() - , end(m_queue.end()); i != end; ++i) - { - if (i->peer.get() == peer) return true; - } - return false; - } + bool is_queued(bandwidth_socket const* peer) const; #endif - int queue_size() const - { - return m_queue.size(); - } - - int queued_bytes() const - { - return m_queued_bytes; - } + int queue_size() const; + int queued_bytes() const; // non prioritized means that, if there's a line for bandwidth, // others will cut in front of the non-prioritized peers. // this is used by web seeds - void request_bandwidth(intrusive_ptr const& peer + void request_bandwidth(intrusive_ptr const& peer , int blk, int priority , bandwidth_channel* chan1 = 0 , bandwidth_channel* chan2 = 0 , bandwidth_channel* chan3 = 0 , bandwidth_channel* chan4 = 0 - , bandwidth_channel* chan5 = 0 - ) - { - INVARIANT_CHECK; - if (m_abort) return; - - TORRENT_ASSERT(blk > 0); - TORRENT_ASSERT(priority > 0); - TORRENT_ASSERT(!is_queued(peer.get())); - - bw_request bwr(peer, blk, priority); - int i = 0; - if (chan1 && chan1->throttle() > 0) bwr.channel[i++] = chan1; - if (chan2 && chan2->throttle() > 0) bwr.channel[i++] = chan2; - if (chan3 && chan3->throttle() > 0) bwr.channel[i++] = chan3; - if (chan4 && chan4->throttle() > 0) bwr.channel[i++] = chan4; - if (chan5 && chan5->throttle() > 0) bwr.channel[i++] = chan5; - if (i == 0) - { - // the connection is not rate limited by any of its - // bandwidth channels, or it doesn't belong to any - // channels. There's no point in adding it to - // the queue, just satisfy the request immediately - bwr.peer->assign_bandwidth(m_channel, blk); - return; - } - m_queued_bytes += blk; - m_queue.push_back(bwr); - } + , bandwidth_channel* chan5 = 0); #ifdef TORRENT_DEBUG - void check_invariant() const - { - int queued = 0; - for (typename queue_t::const_iterator i = m_queue.begin() - , end(m_queue.end()); i != end; ++i) - { - queued += i->request_size - i->assigned; - } - TORRENT_ASSERT(queued == m_queued_bytes); - } + void check_invariant() const; #endif - void update_quotas(time_duration const& dt) - { - if (m_abort) return; - if (m_queue.empty()) return; - - INVARIANT_CHECK; - - int dt_milliseconds = total_milliseconds(dt); - if (dt_milliseconds > 3000) dt_milliseconds = 3000; - - // for each bandwidth channel, call update_quota(dt) - - std::vector channels; - - for (typename queue_t::iterator i = m_queue.begin(); - i != m_queue.end();) - { - if (i->peer->is_disconnecting()) - { - m_queued_bytes -= i->request_size - i->assigned; - - // return all assigned quota to all the - // bandwidth channels this peer belongs to - for (int j = 0; j < 5 && i->channel[j]; ++j) - { - bandwidth_channel* bwc = i->channel[j]; - bwc->return_quota(i->assigned); - } - - i = m_queue.erase(i); - continue; - } - for (int j = 0; j < 5 && i->channel[j]; ++j) - { - bandwidth_channel* bwc = i->channel[j]; - bwc->tmp = 0; - } - ++i; - } - - for (typename queue_t::iterator i = m_queue.begin() - , end(m_queue.end()); i != end; ++i) - { - for (int j = 0; j < 5 && i->channel[j]; ++j) - { - bandwidth_channel* bwc = i->channel[j]; - if (bwc->tmp == 0) channels.push_back(bwc); - bwc->tmp += i->priority; - TORRENT_ASSERT(i->priority > 0); - } - } - - for (std::vector::iterator i = channels.begin() - , end(channels.end()); i != end; ++i) - { - (*i)->update_quota(dt_milliseconds); - } - - queue_t tm; - - for (typename queue_t::iterator i = m_queue.begin(); - i != m_queue.end();) - { - int a = i->assign_bandwidth(); - if (i->assigned == i->request_size - || (i->ttl <= 0 && i->assigned > 0)) - { - a += i->request_size - i->assigned; - TORRENT_ASSERT(i->assigned <= i->request_size); - tm.push_back(*i); - i = m_queue.erase(i); - } - else - { - ++i; - } - m_queued_bytes -= a; - } - - while (!tm.empty()) - { - bw_request& bwr = tm.back(); - bwr.peer->assign_bandwidth(m_channel, bwr.assigned); - tm.pop_back(); - } - } - + void update_quotas(time_duration const& dt); // these are the consumers that want bandwidth - typedef std::vector > queue_t; + typedef std::vector queue_t; queue_t m_queue; // the number of bytes all the requests in queue are for int m_queued_bytes; diff --git a/include/libtorrent/bandwidth_queue_entry.hpp b/include/libtorrent/bandwidth_queue_entry.hpp index ac6d89322..e7dea7eda 100644 --- a/include/libtorrent/bandwidth_queue_entry.hpp +++ b/include/libtorrent/bandwidth_queue_entry.hpp @@ -35,25 +35,16 @@ POSSIBILITY OF SUCH DAMAGE. #include #include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_socket.hpp" namespace libtorrent { -template struct bw_request { - bw_request(boost::intrusive_ptr const& pe - , int blk, int prio) - : peer(pe) - , priority(prio) - , assigned(0) - , request_size(blk) - , ttl(20) - { - TORRENT_ASSERT(priority > 0); - std::memset(channel, 0, sizeof(channel)); - } + bw_request(boost::intrusive_ptr const& pe + , int blk, int prio); - boost::intrusive_ptr peer; + boost::intrusive_ptr peer; // 1 is normal prio int priority; // the number of bytes assigned to this request so far @@ -69,25 +60,7 @@ struct bw_request // loops over the bandwidth channels and assigns bandwidth // from the most limiting one - int assign_bandwidth() - { - TORRENT_ASSERT(assigned < request_size); - int quota = request_size - assigned; - TORRENT_ASSERT(quota >= 0); - for (int j = 0; j < 5 && channel[j]; ++j) - { - if (channel[j]->throttle() == 0) continue; - quota = (std::min)(int(boost::uint64_t(channel[j]->distribute_quota) - * priority / channel[j]->tmp), quota); - } - assigned += quota; - for (int j = 0; j < 5 && channel[j]; ++j) - channel[j]->use_quota(quota); - TORRENT_ASSERT(assigned <= request_size); - --ttl; - TORRENT_ASSERT(assigned <= request_size); - return quota; - } + int assign_bandwidth(); bandwidth_channel* channel[5]; }; diff --git a/include/libtorrent/bandwidth_socket.hpp b/include/libtorrent/bandwidth_socket.hpp new file mode 100644 index 000000000..249f6abe2 --- /dev/null +++ b/include/libtorrent/bandwidth_socket.hpp @@ -0,0 +1,51 @@ +/* + +Copyright (c) 2009, 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_BANDWIDTH_SOCKET_HPP_INCLUDED +#define TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED + +#include "libtorrent/intrusive_ptr_base.hpp" + +namespace libtorrent +{ + struct bandwidth_socket + : public intrusive_ptr_base + { + virtual void assign_bandwidth(int channel, int amount) = 0; + virtual bool is_disconnecting() const = 0; + virtual ~bandwidth_socket() {} + }; +} + +#endif // TORRENT_BANDWIDTH_SOCKET_HPP_INCLUDED + diff --git a/include/libtorrent/peer_connection.hpp b/include/libtorrent/peer_connection.hpp index d85b66491..1e834fa27 100644 --- a/include/libtorrent/peer_connection.hpp +++ b/include/libtorrent/peer_connection.hpp @@ -76,6 +76,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/chained_buffer.hpp" #include "libtorrent/disk_buffer_holder.hpp" #include "libtorrent/bitfield.hpp" +#include "libtorrent/bandwidth_socket.hpp" #ifdef TORRENT_STATS #include "libtorrent/aux_/session_impl.hpp" @@ -127,7 +128,7 @@ namespace libtorrent }; class TORRENT_EXPORT peer_connection - : public intrusive_ptr_base + : public bandwidth_socket , public boost::noncopyable { friend class invariant_access; @@ -639,7 +640,7 @@ namespace libtorrent boost::intrusive_ptr self() { TORRENT_ASSERT(!m_in_constructor); - return intrusive_ptr_base::self(); + return intrusive_ptr(this); } #endif diff --git a/src/Makefile.am b/src/Makefile.am index a6db32383..1e92e5a46 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,6 +21,9 @@ libtorrent_rasterbar_la_SOURCES = \ alert.cpp \ allocator.cpp \ assert.cpp \ + bandwidth_limit \ + bandwidth_manager \ + bandwidth_queue_entry \ broadcast_socket.cpp \ bt_peer_connection.cpp \ connection_queue.cpp \ diff --git a/src/bandwidth_limit.cpp b/src/bandwidth_limit.cpp new file mode 100644 index 000000000..cc0d462dc --- /dev/null +++ b/src/bandwidth_limit.cpp @@ -0,0 +1,87 @@ +/* + +Copyright (c) 2009, 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. + +*/ + +#include "libtorrent/bandwidth_limit.hpp" + +namespace libtorrent +{ + bandwidth_channel::bandwidth_channel() + : m_quota_left(0) + , m_limit(0) + {} + + // 0 means infinite + void bandwidth_channel::throttle(int limit) + { + TORRENT_ASSERT(limit >= 0); + // if the throttle is more than this, we might overflow + TORRENT_ASSERT(limit < INT_MAX / 31); + m_limit = limit; + } + + int bandwidth_channel::quota_left() const + { + if (m_limit == 0) return inf; + return (std::max)(m_quota_left, 0); + } + + void bandwidth_channel::update_quota(int dt_milliseconds) + { + if (m_limit == 0) return; + m_quota_left += (m_limit * dt_milliseconds + 500) / 1000; + if (m_quota_left > m_limit * 3) m_quota_left = m_limit * 3; + distribute_quota = (std::max)(m_quota_left, 0); +// fprintf(stderr, "%p: [%d]: + %d limit: %d\n", this +// , dt_milliseconds, (m_limit * dt_milliseconds + 500) / 1000, m_limit); + } + + // this is used when connections disconnect with + // some quota left. It's returned to its bandwidth + // channels. + void bandwidth_channel::return_quota(int amount) + { + TORRENT_ASSERT(amount >= 0); + if (m_limit == 0) return; + TORRENT_ASSERT(m_quota_left <= m_quota_left + amount); + m_quota_left += amount; + } + + void bandwidth_channel::use_quota(int amount) + { + TORRENT_ASSERT(amount >= 0); + TORRENT_ASSERT(m_limit >= 0); + if (m_limit == 0) return; + m_quota_left -= amount; + } + +} + diff --git a/src/bandwidth_manager.cpp b/src/bandwidth_manager.cpp new file mode 100644 index 000000000..899aca164 --- /dev/null +++ b/src/bandwidth_manager.cpp @@ -0,0 +1,223 @@ +/* + +Copyright (c) 2009, 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. + +*/ + +#include "libtorrent/bandwidth_manager.hpp" + +namespace libtorrent +{ + + bandwidth_manager::bandwidth_manager(int channel +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + , bool log = false +#endif + ) + : m_queued_bytes(0) + , m_channel(channel) + , m_abort(false) + { +#ifdef TORRENT_VERBOSE_BANDWIDTH_LIMIT + if (log) + m_log.open("bandwidth_limiter.log", std::ios::trunc); + m_start = time_now(); +#endif + } + + void bandwidth_manager::close() + { + m_abort = true; + m_queue.clear(); + m_queued_bytes = 0; + error_code ec; + } + +#ifdef TORRENT_DEBUG + bool bandwidth_manager::is_queued(bandwidth_socket const* peer) const + { + for (queue_t::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + if (i->peer.get() == peer) return true; + } + return false; + } +#endif + + int bandwidth_manager::queue_size() const + { + return m_queue.size(); + } + + int bandwidth_manager::queued_bytes() const + { + return m_queued_bytes; + } + + // non prioritized means that, if there's a line for bandwidth, + // others will cut in front of the non-prioritized peers. + // this is used by web seeds + void bandwidth_manager::request_bandwidth(boost::intrusive_ptr const& peer + , int blk, int priority + , bandwidth_channel* chan1 + , bandwidth_channel* chan2 + , bandwidth_channel* chan3 + , bandwidth_channel* chan4 + , bandwidth_channel* chan5 + ) + { + INVARIANT_CHECK; + if (m_abort) return; + + TORRENT_ASSERT(blk > 0); + TORRENT_ASSERT(priority > 0); + TORRENT_ASSERT(!is_queued(peer.get())); + + bw_request bwr(peer, blk, priority); + int i = 0; + if (chan1 && chan1->throttle() > 0) bwr.channel[i++] = chan1; + if (chan2 && chan2->throttle() > 0) bwr.channel[i++] = chan2; + if (chan3 && chan3->throttle() > 0) bwr.channel[i++] = chan3; + if (chan4 && chan4->throttle() > 0) bwr.channel[i++] = chan4; + if (chan5 && chan5->throttle() > 0) bwr.channel[i++] = chan5; + if (i == 0) + { + // the connection is not rate limited by any of its + // bandwidth channels, or it doesn't belong to any + // channels. There's no point in adding it to + // the queue, just satisfy the request immediately + bwr.peer->assign_bandwidth(m_channel, blk); + return; + } + m_queued_bytes += blk; + m_queue.push_back(bwr); + } + +#ifdef TORRENT_DEBUG + void bandwidth_manager::check_invariant() const + { + int queued = 0; + for (queue_t::const_iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + queued += i->request_size - i->assigned; + } + TORRENT_ASSERT(queued == m_queued_bytes); + } +#endif + + void bandwidth_manager::update_quotas(time_duration const& dt) + { + if (m_abort) return; + if (m_queue.empty()) return; + + INVARIANT_CHECK; + + int dt_milliseconds = total_milliseconds(dt); + if (dt_milliseconds > 3000) dt_milliseconds = 3000; + + // for each bandwidth channel, call update_quota(dt) + + std::vector channels; + + for (queue_t::iterator i = m_queue.begin(); + i != m_queue.end();) + { + if (i->peer->is_disconnecting()) + { + m_queued_bytes -= i->request_size - i->assigned; + + // return all assigned quota to all the + // bandwidth channels this peer belongs to + for (int j = 0; j < 5 && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + bwc->return_quota(i->assigned); + } + + i = m_queue.erase(i); + continue; + } + for (int j = 0; j < 5 && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + bwc->tmp = 0; + } + ++i; + } + + for (queue_t::iterator i = m_queue.begin() + , end(m_queue.end()); i != end; ++i) + { + for (int j = 0; j < 5 && i->channel[j]; ++j) + { + bandwidth_channel* bwc = i->channel[j]; + if (bwc->tmp == 0) channels.push_back(bwc); + bwc->tmp += i->priority; + TORRENT_ASSERT(i->priority > 0); + } + } + + for (std::vector::iterator i = channels.begin() + , end(channels.end()); i != end; ++i) + { + (*i)->update_quota(dt_milliseconds); + } + + queue_t tm; + + for (queue_t::iterator i = m_queue.begin(); + i != m_queue.end();) + { + int a = i->assign_bandwidth(); + if (i->assigned == i->request_size + || (i->ttl <= 0 && i->assigned > 0)) + { + a += i->request_size - i->assigned; + TORRENT_ASSERT(i->assigned <= i->request_size); + tm.push_back(*i); + i = m_queue.erase(i); + } + else + { + ++i; + } + m_queued_bytes -= a; + } + + while (!tm.empty()) + { + bw_request& bwr = tm.back(); + bwr.peer->assign_bandwidth(m_channel, bwr.assigned); + tm.pop_back(); + } + } +} + diff --git a/src/bandwidth_queue_entry.cpp b/src/bandwidth_queue_entry.cpp new file mode 100644 index 000000000..2fa9e783e --- /dev/null +++ b/src/bandwidth_queue_entry.cpp @@ -0,0 +1,70 @@ +/* + +Copyright (c) 2009, 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. + +*/ + +#include +#include "libtorrent/bandwidth_queue_entry.hpp" + +namespace libtorrent +{ + bw_request::bw_request(boost::intrusive_ptr const& pe + , int blk, int prio) + : peer(pe) + , priority(prio) + , assigned(0) + , request_size(blk) + , ttl(20) + { + TORRENT_ASSERT(priority > 0); + std::memset(channel, 0, sizeof(channel)); + } + + int bw_request::assign_bandwidth() + { + TORRENT_ASSERT(assigned < request_size); + int quota = request_size - assigned; + TORRENT_ASSERT(quota >= 0); + for (int j = 0; j < 5 && channel[j]; ++j) + { + if (channel[j]->throttle() == 0) continue; + quota = (std::min)(int(boost::uint64_t(channel[j]->distribute_quota) + * priority / channel[j]->tmp), quota); + } + assigned += quota; + for (int j = 0; j < 5 && channel[j]; ++j) + channel[j]->use_quota(quota); + TORRENT_ASSERT(assigned <= request_size); + --ttl; + TORRENT_ASSERT(assigned <= request_size); + return quota; + } +} + diff --git a/test/test_bandwidth_limiter.cpp b/test/test_bandwidth_limiter.cpp index 66a904e3c..e670c68fe 100644 --- a/test/test_bandwidth_limiter.cpp +++ b/test/test_bandwidth_limiter.cpp @@ -35,10 +35,10 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/bandwidth_manager.hpp" #include "libtorrent/bandwidth_queue_entry.hpp" #include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_socket.hpp" #include "libtorrent/socket.hpp" #include "libtorrent/stat.hpp" #include "libtorrent/time.hpp" -#include "libtorrent/intrusive_ptr_base.hpp" #include #include @@ -56,9 +56,9 @@ const float sample_time = 20.f; // seconds bandwidth_channel global_bwc; -struct peer_connection: intrusive_ptr_base +struct peer_connection: bandwidth_socket { - peer_connection(bandwidth_manager& bwm + peer_connection(bandwidth_manager& bwm , bandwidth_channel& torrent_bwc, int prio, bool ignore_limits, std::string name) : m_bwm(bwm) , m_torrent_bandwidth_channel(torrent_bwc) @@ -76,7 +76,7 @@ struct peer_connection: intrusive_ptr_base void start(); - bandwidth_manager& m_bwm; + bandwidth_manager& m_bwm; bandwidth_channel m_bandwidth_channel; bandwidth_channel& m_torrent_bandwidth_channel; @@ -143,7 +143,7 @@ void do_change_peer_rate(connections_t& v, int limit) void nop() {} void run_test(connections_t& v - , bandwidth_manager& manager + , bandwidth_manager& manager , boost::function f = &nop) { std::cerr << "-------------" << std::endl; @@ -163,7 +163,7 @@ bool close_to(float val, float comp, float err) return fabs(val - comp) <= err; } -void spawn_connections(connections_t& v, bandwidth_manager& bwm +void spawn_connections(connections_t& v, bandwidth_manager& bwm , bandwidth_channel& bwc, int num, char const* prefix) { for (int i = 0; i < num; ++i) @@ -176,7 +176,7 @@ void spawn_connections(connections_t& v, bandwidth_manager& bwm void test_equal_connections(int num, int limit) { std::cerr << "\ntest equal connections " << num << " " << limit << std::endl; - bandwidth_manager manager(0); + bandwidth_manager manager(0); global_bwc.throttle(limit); bandwidth_channel t1; @@ -208,7 +208,7 @@ void test_connections_variable_rate(int num, int limit, int torrent_limit) << " l: " << limit << " t: " << torrent_limit << std::endl; - bandwidth_manager manager(0); + bandwidth_manager manager(0); global_bwc.throttle(0); bandwidth_channel t1; @@ -246,7 +246,7 @@ void test_connections_variable_rate(int num, int limit, int torrent_limit) void test_single_peer(int limit, bool torrent_limit) { std::cerr << "\ntest single peer " << limit << " " << torrent_limit << std::endl; - bandwidth_manager manager(0); + bandwidth_manager manager(0); bandwidth_channel t1; global_bwc.throttle(0); @@ -277,7 +277,7 @@ void test_torrents(int num, int limit1, int limit2, int global_limit) << " l1: " << limit1 << " l2: " << limit2 << " g: " << global_limit << std::endl; - bandwidth_manager manager(0); + bandwidth_manager manager(0); global_bwc.throttle(global_limit); bandwidth_channel t1; @@ -328,7 +328,7 @@ void test_torrents_variable_rate(int num, int limit, int global_limit) std::cerr << "\ntest torrents variable rate" << num << " l: " << limit << " g: " << global_limit << std::endl; - bandwidth_manager manager(0); + bandwidth_manager manager(0); global_bwc.throttle(global_limit); bandwidth_channel t1; @@ -376,7 +376,7 @@ void test_torrents_variable_rate(int num, int limit, int global_limit) void test_peer_priority(int limit, bool torrent_limit) { std::cerr << "\ntest peer priority " << limit << " " << torrent_limit << std::endl; - bandwidth_manager manager(0); + bandwidth_manager manager(0); bandwidth_channel t1; global_bwc.throttle(0); @@ -413,7 +413,7 @@ void test_peer_priority(int limit, bool torrent_limit) void test_no_starvation(int limit) { std::cerr << "\ntest no starvation " << limit << std::endl; - bandwidth_manager manager(0); + bandwidth_manager manager(0); bandwidth_channel t1; bandwidth_channel t2;