separated bandwidth_manager into bandwidht_limit and bandwidth_queue_entry and fixed problems related to #225

This commit is contained in:
Arvid Norberg 2007-12-31 09:41:50 +00:00
parent 62670cc2f3
commit ddf16fb3bb
8 changed files with 456 additions and 154 deletions

View File

@ -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 <boost/integer_traits.hpp>
#include "libtorrent/assert.hpp"
namespace libtorrent {
// member of peer_connection
struct bandwidth_limit
{
static const int inf = boost::integer_traits<int>::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

View File

@ -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<Torrent> tor;
};
template<class PeerConnection>
struct bw_queue_entry
{
bw_queue_entry(boost::intrusive_ptr<PeerConnection> const& pe
, int blk, bool no_prio)
: peer(pe), max_block_size(blk), non_prioritized(no_prio) {}
boost::intrusive_ptr<PeerConnection> peer;
int max_block_size;
bool non_prioritized;
};
// member of peer_connection
struct bandwidth_limit
{
static const int inf = boost::integer_traits<int>::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<class T>
T clamp(T val, T ceiling, T floor) throw()
{
@ -204,15 +121,25 @@ struct bandwidth_manager
}
#ifndef NDEBUG
bool is_queued(PeerConnection const* peer)
bool is_queued(PeerConnection const* peer) const
{
for (typename queue_t::iterator i = m_queue.begin()
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_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
@ -231,15 +158,9 @@ struct bandwidth_manager
// make sure this peer isn't already in line
// waiting for bandwidth
TORRENT_ASSERT(!is_queued(peer.get()));
TORRENT_ASSERT(peer->max_assignable_bandwidth(m_channel) > 0);
boost::shared_ptr<Torrent> t = peer->associated_torrent().lock();
if (peer->max_assignable_bandwidth(m_channel) == 0)
{
t->expire_bandwidth(m_channel, blk);
peer->assign_bandwidth(m_channel, 0);
return;
}
m_queue.push_back(bw_queue_entry<PeerConnection>(peer, blk, non_prioritized));
if (!non_prioritized)
{
@ -360,12 +281,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<PeerConnection> qe = m_queue.front();
bw_queue_entry<PeerConnection> qe = q.front();
TORRENT_ASSERT(qe.max_block_size > 0);
m_queue.pop_front();
q.pop_front();
shared_ptr<Torrent> t = qe.peer->associated_torrent().lock();
if (!t) continue;
@ -377,17 +307,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;
}
@ -432,7 +358,7 @@ private:
#endif
if (amount < block_size / 2)
{
m_queue.push_front(qe);
tmp.push_back(qe);
break;
}
@ -451,6 +377,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());
m_in_hand_out_bandwidth = false;
}

View File

@ -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 <boost/intrusive_ptr.hpp>
namespace libtorrent {
template<class PeerConnection>
struct bw_queue_entry
{
bw_queue_entry(boost::intrusive_ptr<PeerConnection> const& pe
, int blk, bool no_prio)
: peer(pe), max_block_size(blk), non_prioritized(no_prio) {}
boost::intrusive_ptr<PeerConnection> peer;
int max_block_size;
bool non_prioritized;
};
}
#endif

View File

@ -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"

View File

@ -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"

View File

@ -2916,6 +2916,12 @@ 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));
}
if (m_peer_info)
{
TORRENT_ASSERT(m_peer_info->connection == this

View File

@ -2264,6 +2264,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;
@ -2288,16 +2289,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<peer_connection> 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

View File

@ -4,6 +4,7 @@
#include "libtorrent/socket.hpp"
#include "libtorrent/stat.hpp"
#include "libtorrent/time.hpp"
#include "libtorrent/intrusive_ptr_base.hpp"
#include <boost/lexical_cast.hpp>
@ -28,8 +29,8 @@ struct torrent
std::cerr << time_now_string()
<< ": assign bandwidth, " << amount << " blk: " << max_block_size << std::endl;
#endif
assert(amount > 0);
assert(amount <= max_block_size);
TEST_CHECK(amount > 0);
TEST_CHECK(amount <= max_block_size);
if (amount < max_block_size)
expire_bandwidth(channel, max_block_size - amount);
}
@ -44,7 +45,7 @@ struct torrent
, boost::intrusive_ptr<peer_connection> const& p
, bool non_prioritized)
{
assert(m_bandwidth_limit[channel].throttle() > 0);
TEST_CHECK(m_bandwidth_limit[channel].throttle() > 0);
int block_size = m_bandwidth_limit[channel].throttle() / 10;
if (block_size <= 0) block_size = 1;
@ -54,7 +55,6 @@ struct torrent
std::cerr << time_now_string()
<< ": request bandwidth " << block_size << std::endl;
#endif
perform_bandwidth_request(channel, p, block_size, non_prioritized);
}
else
@ -73,24 +73,7 @@ struct torrent
}
}
void expire_bandwidth(int channel, int amount)
{
#ifdef VERBOSE_LOGGING
std::cerr << time_now_string()
<< ": expire bandwidth, " << amount << std::endl;
#endif
assert(amount > 0);
m_bandwidth_limit[channel].expire(amount);
while (!m_bandwidth_queue[channel].empty())
{
bw_queue_entry<peer_connection> qe = m_bandwidth_queue[channel].front();
if (m_bandwidth_limit[channel].max_assignable() == 0)
break;
m_bandwidth_queue[channel].pop_front();
perform_bandwidth_request(channel, qe.peer
, qe.max_block_size, qe.non_prioritized);
}
}
void expire_bandwidth(int channel, int amount);
void perform_bandwidth_request(int channel
, boost::intrusive_ptr<peer_connection> const& p
@ -107,7 +90,7 @@ struct torrent
bandwidth_manager<peer_connection, torrent>& m_bandwidth_manager;
};
struct peer_connection
struct peer_connection: intrusive_ptr_base<peer_connection>
{
peer_connection(io_service& ios, boost::shared_ptr<torrent> const& t
, bool prio, bool ignore_limits, std::string name)
@ -117,7 +100,7 @@ struct peer_connection
, m_abort(false)
, m_ios(ios)
, m_name(name)
, m_refs(0)
, m_writing(false)
{}
bool ignore_bandwidth_limits() { return m_ignore_limits; }
@ -128,33 +111,53 @@ struct peer_connection
bool is_disconnecting() const { return m_abort; }
void assign_bandwidth(int channel, int amount)
{
TEST_CHECK(m_writing);
#ifdef VERBOSE_LOGGING
std::cerr << time_now_string() << ": [" << m_name
<< "] assign bandwidth, " << amount << std::endl;
#endif
assert(amount > 0);
TEST_CHECK(amount > 0);
m_bandwidth_limit[channel].assign(amount);
m_ios.post(boost::bind(&peer_connection::on_transfer, self(), channel, amount));
}
void on_transfer(int channel, int amount)
{
TEST_CHECK(m_writing);
m_writing = false;
m_stats.sent_bytes(amount, 0);
boost::shared_ptr<torrent> t = m_torrent.lock();
if (!t) return;
m_stats.sent_bytes(amount, 0);
m_ios.post(boost::bind(&torrent::request_bandwidth, t, int(0)
, intrusive_ptr<peer_connection>(this), !m_prioritized));
if (m_bandwidth_limit[channel].max_assignable() > 0)
{
m_writing = true;
t->request_bandwidth(0, this, !m_prioritized);
}
}
void start()
{
boost::shared_ptr<torrent> t = m_torrent.lock();
if (!t) return;
m_writing = true;
t->request_bandwidth(0, this, !m_prioritized);
}
void stop() { m_abort = true; }
void expire_bandwidth(int channel, int amount)
{
assert(amount > 0);
TEST_CHECK(amount > 0);
#ifdef VERBOSE_LOGGING
std::cerr << time_now_string() << ": [" << m_name
<< "] expire bandwidth, " << amount << std::endl;
#endif
m_bandwidth_limit[channel].expire(amount);
if (!m_writing && m_bandwidth_limit[channel].max_assignable() > 0)
{
boost::shared_ptr<torrent> t = m_torrent.lock();
if (!t) return;
m_writing = true;
t->request_bandwidth(0, this, !m_prioritized);
}
}
void tick()
{
@ -168,6 +171,8 @@ struct peer_connection
int bandwidth_throttle(int channel) const
{ return m_bandwidth_limit[channel].throttle(); }
void throttle(int limit) { m_bandwidth_limit[0].throttle(limit); }
bandwidth_limit m_bandwidth_limit[1];
boost::weak_ptr<torrent> m_torrent;
bool m_prioritized;
@ -176,18 +181,34 @@ struct peer_connection
libtorrent::stat m_stats;
io_service& m_ios;
std::string m_name;
int m_refs;
bool m_writing;
};
void intrusive_ptr_add_ref(peer_connection* p)
void torrent::expire_bandwidth(int channel, int amount)
{
++p->m_refs;
}
void intrusive_ptr_release(peer_connection* p)
{
if (--p->m_refs == 0)
delete p;
#ifdef VERBOSE_LOGGING
std::cerr << time_now_string()
<< ": expire bandwidth, " << amount << std::endl;
#endif
TEST_CHECK(amount > 0);
m_bandwidth_limit[channel].expire(amount);
queue_t tmp;
while (!m_bandwidth_queue[channel].empty())
{
bw_queue_entry<peer_connection> 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_bandwidth_manager.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());
}
typedef std::vector<boost::intrusive_ptr<peer_connection> > connections_t;
@ -216,10 +237,56 @@ void do_stop(deadline_timer& tick, connections_t& v)
std::cerr << " stopping..." << std::endl;
}
void do_change_rate(asio::error_code const&e, deadline_timer& tick
, boost::shared_ptr<torrent> t1
, boost::shared_ptr<torrent> t2
, int limit
, int counter)
{
TEST_CHECK(!e);
if (e) return;
if (counter == 0)
{
t1->m_bandwidth_limit[0].throttle(limit);
t2->m_bandwidth_limit[0].throttle(limit);
return;
}
t1->m_bandwidth_limit[0].throttle(limit + limit / 2 * ((counter & 1)?-1:1));
t2->m_bandwidth_limit[0].throttle(limit + limit / 2 * ((counter & 1)?1:-1));
tick.expires_from_now(milliseconds(1600));
tick.async_wait(boost::bind(&do_change_rate, _1, boost::ref(tick), t1, t2, limit, counter-1));
}
void do_change_peer_rate(asio::error_code const&e, deadline_timer& tick
, connections_t& v
, int limit
, int counter)
{
TEST_CHECK(!e);
if (e) return;
if (counter == 0)
{
std::for_each(v.begin(), v.end()
, boost::bind(&peer_connection::throttle, _1, limit));
return;
}
int c = counter;
for (connections_t::iterator i = v.begin(); i != v.end(); ++i, ++c)
i->get()->throttle(limit + limit / 2 * ((c & 1)?-1:1));
tick.expires_from_now(milliseconds(1100));
tick.async_wait(boost::bind(&do_change_peer_rate, _1, boost::ref(tick), boost::ref(v), limit, counter-1));
}
void run_test(io_service& ios, connections_t& v)
{
abort_tick = false;
std::cerr << "-------------\n" << std::endl;
std::cerr << "-------------" << std::endl;
deadline_timer tick(ios);
tick.expires_from_now(seconds(1));
tick.async_wait(boost::bind(&do_tick, _1, boost::ref(tick), boost::ref(v)));
@ -251,7 +318,7 @@ void spawn_connections(connections_t& v, io_service& ios
void test_equal_connections(int num, int limit)
{
std::cerr << "test equal connections " << num << " " << limit << std::endl;
std::cerr << "\ntest equal connections " << num << " " << limit << std::endl;
io_service ios;
bandwidth_manager<peer_connection, torrent> manager(ios, 0);
manager.throttle(limit);
@ -279,9 +346,53 @@ void test_equal_connections(int num, int limit)
TEST_CHECK(close_to(sum, limit, 50));
}
void test_connections_variable_rate(int num, int limit, int torrent_limit)
{
std::cerr << "\ntest connections variable rate" << num
<< " l: " << limit
<< " t: " << torrent_limit
<< std::endl;
io_service ios;
bandwidth_manager<peer_connection, torrent> manager(ios, 0);
boost::shared_ptr<torrent> t1(new torrent(manager));
if (torrent_limit)
t1->m_bandwidth_limit[0].throttle(torrent_limit);
connections_t v;
spawn_connections(v, ios, t1, num, "p");
std::for_each(v.begin(), v.end()
, boost::bind(&peer_connection::throttle, _1, limit));
deadline_timer change_rate(ios);
change_rate.expires_from_now(milliseconds(1600));
change_rate.async_wait(boost::bind(&do_change_peer_rate, _1, boost::ref(change_rate)
, boost::ref(v), limit, 9));
run_test(ios, v);
if (torrent_limit > 0 && limit * num > torrent_limit)
limit = torrent_limit / num;
float sum = 0.f;
float err = limit * 0.3f;
for (connections_t::iterator i = v.begin()
, end(v.end()); i != end; ++i)
{
sum += (*i)->m_stats.total_payload_upload();
std::cerr << (*i)->m_stats.total_payload_upload() / sample_time
<< " target: " << limit << " eps: " << err << std::endl;
TEST_CHECK(close_to((*i)->m_stats.total_payload_upload() / sample_time, limit, err));
}
sum /= sample_time;
std::cerr << "sum: " << sum << " target: " << (limit * num) << std::endl;
TEST_CHECK(sum > 0);
TEST_CHECK(close_to(sum, limit * num, limit * 0.3f * num));
}
void test_single_peer(int limit, bool torrent_limit)
{
std::cerr << "test single peer " << limit << " " << torrent_limit << std::endl;
std::cerr << "\ntest single peer " << limit << " " << torrent_limit << std::endl;
io_service ios;
bandwidth_manager<peer_connection, torrent> manager(ios, 0);
boost::shared_ptr<torrent> t1(new torrent(manager));
@ -309,7 +420,10 @@ void test_single_peer(int limit, bool torrent_limit)
void test_torrents(int num, int limit1, int limit2, int global_limit)
{
std::cerr << "test equal torrents " << num << " " << limit1 << " " << limit2 << std::endl;
std::cerr << "\ntest equal torrents " << num
<< " l1: " << limit1
<< " l2: " << limit2
<< " g: " << global_limit << std::endl;
io_service ios;
bandwidth_manager<peer_connection, torrent> manager(ios, 0);
if (global_limit > 0)
@ -330,6 +444,11 @@ void test_torrents(int num, int limit1, int limit2, int global_limit)
std::copy(v2.begin(), v2.end(), std::back_inserter(v));
run_test(ios, v);
if (global_limit > 0 && global_limit < limit1 + limit2)
{
limit1 = (std::min)(limit1, global_limit / 2);
limit2 = global_limit - limit1;
}
float sum = 0.f;
for (connections_t::iterator i = v1.begin()
, end(v1.end()); i != end; ++i)
@ -353,9 +472,65 @@ void test_torrents(int num, int limit1, int limit2, int global_limit)
TEST_CHECK(close_to(sum, limit2, 1000));
}
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;
io_service ios;
bandwidth_manager<peer_connection, torrent> manager(ios, 0);
if (global_limit > 0)
manager.throttle(global_limit);
boost::shared_ptr<torrent> t1(new torrent(manager));
boost::shared_ptr<torrent> t2(new torrent(manager));
t1->m_bandwidth_limit[0].throttle(limit);
t2->m_bandwidth_limit[0].throttle(limit);
connections_t v1;
spawn_connections(v1, ios, t1, num, "t1p");
connections_t v2;
spawn_connections(v2, ios, t2, num, "t2p");
connections_t v;
std::copy(v1.begin(), v1.end(), std::back_inserter(v));
std::copy(v2.begin(), v2.end(), std::back_inserter(v));
deadline_timer change_rate(ios);
change_rate.expires_from_now(milliseconds(1100));
change_rate.async_wait(boost::bind(&do_change_rate, _1, boost::ref(change_rate), t1, t2, limit, 9));
run_test(ios, v);
if (global_limit > 0 && global_limit < 2 * limit)
limit = global_limit / 2;
float sum = 0.f;
for (connections_t::iterator i = v1.begin()
, end(v1.end()); i != end; ++i)
{
sum += (*i)->m_stats.total_payload_upload();
}
sum /= sample_time;
std::cerr << sum << " target: " << limit << std::endl;
TEST_CHECK(sum > 0);
TEST_CHECK(close_to(sum, limit, 1000));
sum = 0.f;
for (connections_t::iterator i = v2.begin()
, end(v2.end()); i != end; ++i)
{
sum += (*i)->m_stats.total_payload_upload();
}
sum /= sample_time;
std::cerr << sum << " target: " << limit << std::endl;
TEST_CHECK(sum > 0);
TEST_CHECK(close_to(sum, limit, 1000));
}
void test_peer_priority(int limit, bool torrent_limit)
{
std::cerr << "test peer priority " << limit << " " << torrent_limit << std::endl;
std::cerr << "\ntest peer priority " << limit << " " << torrent_limit << std::endl;
io_service ios;
bandwidth_manager<peer_connection, torrent> manager(ios, 0);
boost::shared_ptr<torrent> t1(new torrent(manager));
@ -391,7 +566,7 @@ void test_peer_priority(int limit, bool torrent_limit)
void test_no_starvation(int limit)
{
std::cerr << "test no starvation " << limit << std::endl;
std::cerr << "\ntest no starvation " << limit << std::endl;
io_service ios;
bandwidth_manager<peer_connection, torrent> manager(ios, 0);
boost::shared_ptr<torrent> t1(new torrent(manager));
@ -437,11 +612,21 @@ int test_main()
test_equal_connections(7, 20000);
test_equal_connections(33, 60000);
test_equal_connections(33, 500000);
test_connections_variable_rate(2, 20, 0);
test_connections_variable_rate(5, 20000, 0);
test_connections_variable_rate(3, 2000, 6000);
test_connections_variable_rate(5, 2000, 30000);
test_connections_variable_rate(33, 500000, 0);
test_torrents(2, 400, 400, 0);
test_torrents(2, 100, 500, 0);
test_torrents(2, 3000, 3000, 6000);
test_torrents(1, 40000, 40000, 0);
test_torrents(24, 50000, 50000, 0);
test_torrents(5, 6000, 6000, 3000);
test_torrents(5, 6000, 5000, 4000);
test_torrents(5, 20000, 20000, 30000);
test_torrents_variable_rate(5, 6000, 3000);
test_torrents_variable_rate(5, 20000, 30000);
test_single_peer(40000, true);
test_single_peer(40000, false);
test_peer_priority(40000, false);