diff --git a/libtorrent/include/libtorrent/bandwidth_limit.hpp b/libtorrent/include/libtorrent/bandwidth_limit.hpp new file mode 100644 index 000000000..e0675aa31 --- /dev/null +++ b/libtorrent/include/libtorrent/bandwidth_limit.hpp @@ -0,0 +1,120 @@ +/* + +Copyright (c) 2007, 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_LIMIT_HPP_INCLUDED +#define TORRENT_BANDWIDTH_LIMIT_HPP_INCLUDED + +#include + +#include "libtorrent/assert.hpp" + +namespace libtorrent { + +// member of peer_connection +struct bandwidth_limit +{ + static const int inf = boost::integer_traits::const_max; + + bandwidth_limit() + : m_quota_left(0) + , m_local_limit(inf) + , m_current_rate(0) + {} + + void throttle(int limit) + { + TORRENT_ASSERT(limit > 0); + m_local_limit = limit; + } + + int throttle() const + { + return m_local_limit; + } + + void assign(int amount) + { + TORRENT_ASSERT(amount >= 0); + m_current_rate += amount; + m_quota_left += amount; + } + + void use_quota(int amount) + { + TORRENT_ASSERT(amount <= m_quota_left); + m_quota_left -= amount; + } + + int quota_left() const + { + return (std::max)(m_quota_left, 0); + } + + void expire(int amount) + { + TORRENT_ASSERT(amount >= 0); + m_current_rate -= amount; + } + + int max_assignable() const + { + if (m_local_limit == inf) return inf; + if (m_local_limit <= m_current_rate) return 0; + return m_local_limit - m_current_rate; + } + +private: + + // this is the amount of bandwidth we have + // been assigned without using yet. i.e. + // the bandwidth that we use up every time + // we receive or send a message. Once this + // hits zero, we need to request more + // bandwidth from the torrent which + // in turn will request bandwidth from + // the bandwidth manager + int m_quota_left; + + // the local limit is the number of bytes + // per window size we are allowed to use. + int m_local_limit; + + // the current rate is the number of + // bytes we have been assigned within + // the window size. + int m_current_rate; +}; + +} + +#endif + diff --git a/libtorrent/include/libtorrent/bandwidth_manager.hpp b/libtorrent/include/libtorrent/bandwidth_manager.hpp index da251cf4b..3458ec2a0 100644 --- a/libtorrent/include/libtorrent/bandwidth_manager.hpp +++ b/libtorrent/include/libtorrent/bandwidth_manager.hpp @@ -44,6 +44,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/assert.hpp" +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_queue_entry.hpp" using boost::weak_ptr; using boost::shared_ptr; @@ -77,91 +79,6 @@ struct history_entry weak_ptr tor; }; -template -struct bw_queue_entry -{ - bw_queue_entry(boost::intrusive_ptr const& pe - , int blk, bool no_prio) - : peer(pe), max_block_size(blk), non_prioritized(no_prio) {} - boost::intrusive_ptr peer; - int max_block_size; - bool non_prioritized; -}; - -// member of peer_connection -struct bandwidth_limit -{ - static const int inf = boost::integer_traits::const_max; - - bandwidth_limit() throw() - : m_quota_left(0) - , m_local_limit(inf) - , m_current_rate(0) - {} - - void throttle(int limit) throw() - { - m_local_limit = limit; - } - - int throttle() const throw() - { - return m_local_limit; - } - - void assign(int amount) throw() - { - TORRENT_ASSERT(amount >= 0); - m_current_rate += amount; - m_quota_left += amount; - } - - void use_quota(int amount) throw() - { - TORRENT_ASSERT(amount <= m_quota_left); - m_quota_left -= amount; - } - - int quota_left() const throw() - { - return (std::max)(m_quota_left, 0); - } - - void expire(int amount) throw() - { - TORRENT_ASSERT(amount >= 0); - m_current_rate -= amount; - } - - int max_assignable() const throw() - { - if (m_local_limit == inf) return inf; - if (m_local_limit <= m_current_rate) return 0; - return m_local_limit - m_current_rate; - } - -private: - - // this is the amount of bandwidth we have - // been assigned without using yet. i.e. - // the bandwidth that we use up every time - // we receive or send a message. Once this - // hits zero, we need to request more - // bandwidth from the torrent which - // in turn will request bandwidth from - // the bandwidth manager - int m_quota_left; - - // the local limit is the number of bytes - // per window size we are allowed to use. - int m_local_limit; - - // the current rate is the number of - // bytes we have been assigned within - // the window size. - int m_current_rate; -}; - template T clamp(T val, T ceiling, T floor) throw() { @@ -203,6 +120,18 @@ struct bandwidth_manager m_history_timer.cancel(); } +#ifndef NDEBUG + bool is_in_history(PeerConnection const* peer) const + { + for (typename history_t::const_iterator i + = m_history.begin(), end(m_history.end()); i != end; ++i) + { + if (i->peer.get() == peer) return true; + } + return false; + } +#endif + // 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 @@ -224,6 +153,7 @@ struct bandwidth_manager TORRENT_ASSERT(i->peer < peer || peer < i->peer); } #endif + TORRENT_ASSERT(peer->max_assignable_bandwidth(m_channel) > 0); boost::shared_ptr t = peer->associated_torrent().lock(); @@ -369,12 +299,21 @@ private: << " m_current_quota = " << m_current_quota << std::endl; #endif - while (!m_queue.empty() && amount > 0) + if (amount <= 0) + { + m_in_hand_out_bandwidth = false; + return; + } + + queue_t q; + queue_t tmp; + m_queue.swap(q); + while (!q.empty() && amount > 0) { TORRENT_ASSERT(amount == limit - m_current_quota); - bw_queue_entry qe = m_queue.front(); + bw_queue_entry qe = q.front(); TORRENT_ASSERT(qe.max_block_size > 0); - m_queue.pop_front(); + q.pop_front(); shared_ptr t = qe.peer->associated_torrent().lock(); if (!t) continue; @@ -386,17 +325,13 @@ private: } // at this point, max_assignable may actually be zero. Since - // the bandwidth quota is subtracted once the data has been - // sent. If the peer was added to the queue while the data was - // still being sent, max_assignable may have been > 0 at that time. - int max_assignable = (std::min)( - qe.peer->max_assignable_bandwidth(m_channel) - , t->max_assignable_bandwidth(m_channel)); + // the rate limit of the peer might have changed while it + // was in the queue. + int max_assignable = qe.peer->max_assignable_bandwidth(m_channel); if (max_assignable == 0) { - t->expire_bandwidth(m_channel, qe.max_block_size); - qe.peer->assign_bandwidth(m_channel, 0); - TORRENT_ASSERT(amount == limit - m_current_quota); + TORRENT_ASSERT(is_in_history(qe.peer.get())); + tmp.push_back(qe); continue; } @@ -441,7 +376,7 @@ private: #endif if (amount < block_size / 2) { - m_queue.push_front(qe); + tmp.push_back(qe); break; } @@ -460,6 +395,8 @@ private: qe.peer, t, hand_out_amount, now + bw_window_size)); TORRENT_ASSERT(amount == limit - m_current_quota); } + if (!q.empty()) m_queue.insert(m_queue.begin(), q.begin(), q.end()); + if (!tmp.empty()) m_queue.insert(m_queue.begin(), tmp.begin(), tmp.end()); #ifndef NDEBUG } catch (std::exception& e) diff --git a/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp b/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp new file mode 100644 index 000000000..76c119d96 --- /dev/null +++ b/libtorrent/include/libtorrent/bandwidth_queue_entry.hpp @@ -0,0 +1,54 @@ +/* + +Copyright (c) 2007, 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_QUEUE_ENTRY_HPP_INCLUDED +#define TORRENT_BANDWIDTH_QUEUE_ENTRY_HPP_INCLUDED + +#include + +namespace libtorrent { + +template +struct bw_queue_entry +{ + bw_queue_entry(boost::intrusive_ptr const& pe + , int blk, bool no_prio) + : peer(pe), max_block_size(blk), non_prioritized(no_prio) {} + boost::intrusive_ptr peer; + int max_block_size; + bool non_prioritized; +}; + +} + +#endif + diff --git a/libtorrent/include/libtorrent/peer_connection.hpp b/libtorrent/include/libtorrent/peer_connection.hpp index 805b38d9d..60cb4bc17 100755 --- a/libtorrent/include/libtorrent/peer_connection.hpp +++ b/libtorrent/include/libtorrent/peer_connection.hpp @@ -69,7 +69,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/piece_block_progress.hpp" #include "libtorrent/config.hpp" #include "libtorrent/session.hpp" -#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/bandwidth_limit.hpp" #include "libtorrent/policy.hpp" #include "libtorrent/socket_type.hpp" #include "libtorrent/intrusive_ptr_base.hpp" diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index 4731da6b2..f66620999 100755 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -65,7 +65,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/piece_picker.hpp" #include "libtorrent/config.hpp" #include "libtorrent/escape_string.hpp" -#include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/bandwidth_limit.hpp" +#include "libtorrent/bandwidth_queue_entry.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/assert.hpp" diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index e63788d0f..6550a1e4f 100755 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -1399,10 +1399,8 @@ namespace libtorrent if (t->alerts().should_post(alert::fatal)) { - if (j.str != "write failed: No space left on device"){ - std::string err = "torrent paused: disk write error, " + j.str; - t->alerts().post_alert(file_error_alert(t->get_handle(), err)); - } + std::string err = "torrent paused: disk write error, " + j.str; + t->alerts().post_alert(file_error_alert(t->get_handle(), err)); } t->pause(); return; @@ -2564,6 +2562,7 @@ namespace libtorrent // return value is destructed buffer::interval peer_connection::allocate_send_buffer(int size) { + TORRENT_ASSERT(size > 0); char* insert = m_send_buffer.allocate_appendix(size); if (insert == 0) { @@ -2871,6 +2870,13 @@ namespace libtorrent #ifndef NDEBUG void peer_connection::check_invariant() const { + for (int i = 0; i < 2; ++i) + { + // this peer is in the bandwidth history iff max_assignable < limit + TORRENT_ASSERT((m_bandwidth_limit[i].max_assignable() < m_bandwidth_limit[i].throttle()) + == m_ses.m_bandwidth_manager[i]->is_in_history(this) + || m_bandwidth_limit[i].throttle() == bandwidth_limit::inf); + } if (m_peer_info) { TORRENT_ASSERT(m_peer_info->connection == this diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index 7b5da1298..01c396c4a 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -2225,6 +2225,7 @@ namespace libtorrent , bool non_prioritized) { TORRENT_ASSERT(m_bandwidth_limit[channel].throttle() > 0); + TORRENT_ASSERT(p->max_assignable_bandwidth(channel) > 0); int block_size = m_bandwidth_limit[channel].throttle() / 10; if (block_size <= 0) block_size = 1; @@ -2249,16 +2250,23 @@ namespace libtorrent TORRENT_ASSERT(amount > 0); m_bandwidth_limit[channel].expire(amount); - + queue_t tmp; while (!m_bandwidth_queue[channel].empty()) { bw_queue_entry qe = m_bandwidth_queue[channel].front(); if (m_bandwidth_limit[channel].max_assignable() == 0) break; m_bandwidth_queue[channel].pop_front(); + if (qe.peer->max_assignable_bandwidth(channel) <= 0) + { + TORRENT_ASSERT(m_ses.m_bandwidth_manager[channel]->is_in_history(qe.peer.get())); + if (!qe.peer->is_disconnecting()) tmp.push_back(qe); + continue; + } perform_bandwidth_request(channel, qe.peer , qe.max_block_size, qe.non_prioritized); } + m_bandwidth_queue[channel].insert(m_bandwidth_queue[channel].begin(), tmp.begin(), tmp.end()); } void torrent::perform_bandwidth_request(int channel