removed allocate_resources. switched to a global unchoker and connection distribution

This commit is contained in:
Arvid Norberg
2007-08-16 12:41:46 +00:00
parent b62bb7944f
commit 4ac1ac8a1f
20 changed files with 350 additions and 1028 deletions

View File

@@ -171,7 +171,6 @@ lib wsock32 : : <name>wsock32 ;
lib ws2_32 : : <name>ws2_32 ; lib ws2_32 : : <name>ws2_32 ;
SOURCES = SOURCES =
allocate_resources
alert alert
connection_queue connection_queue
entry entry

View File

@@ -1,78 +0,0 @@
/*
Copyright (c) 2003, Magnus Jonsson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
#define TORRENT_ALLOCATE_RESOURCES_HPP_INCLUDED
#include <map>
#include <utility>
#include <boost/shared_ptr.hpp>
#include "libtorrent/resource_request.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/session.hpp"
namespace libtorrent
{
class peer_connection;
class torrent;
int saturated_add(int a, int b);
// Function to allocate a limited resource fairly among many consumers.
// It takes into account the current use, and the consumer's desired use.
// Should be invoked periodically to allow it adjust to the situation (make
// sure "used" is updated between calls!).
// If resources = std::numeric_limits<int>::max() it means there is an infinite
// supply of resources (so everyone can get what they want).
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& torrents
, resource_request torrent::* res);
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& connections
, resource_request peer_connection::* res);
// Used for global limits.
void allocate_resources(
int resources
, std::vector<session*>& _sessions
, resource_request session::* res);
}
#endif

View File

@@ -1,328 +0,0 @@
/*
Copyright (c) 2003, Magnus Jonsson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED
#define TORRENT_ALLOCATE_RESOURCES_IMPL_HPP_INCLUDED
#include <map>
#include <utility>
#include <boost/shared_ptr.hpp>
#include "libtorrent/resource_request.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/size_type.hpp"
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
namespace libtorrent
{
int saturated_add(int a, int b);
namespace aux
{
// give num_resources to r,
// return how how many were actually accepted.
inline int give(resource_request& r, int num_resources)
{
assert(num_resources >= 0);
assert(r.given <= r.max);
int accepted = (std::min)(num_resources, r.max - r.given);
assert(accepted >= 0);
r.given += accepted;
assert(r.given <= r.max);
return accepted;
}
inline int div_round_up(int numerator, int denominator)
{
return (numerator + denominator - 1) / denominator;
}
#ifndef NDEBUG
template<class It, class T>
class allocate_resources_contract_check
{
int m_resources;
It m_start;
It m_end;
resource_request T::* m_res;
public:
allocate_resources_contract_check(
int resources
, It start
, It end
, resource_request T::* res)
: m_resources(resources)
, m_start(start)
, m_end(end)
, m_res(res)
{
assert(m_resources >= 0);
for (It i = m_start, end(m_end); i != end; ++i)
{
assert(((*i).*m_res).max >= 0);
assert(((*i).*m_res).given >= 0);
}
}
~allocate_resources_contract_check()
{
int sum_given = 0;
int sum_max = 0;
int sum_min = 0;
for (It i = m_start, end(m_end); i != end; ++i)
{
assert(((*i).*m_res).max >= 0);
assert(((*i).*m_res).min >= 0);
assert(((*i).*m_res).max >= ((*i).*m_res).min);
assert(((*i).*m_res).given >= 0);
assert(((*i).*m_res).given <= ((*i).*m_res).max);
sum_given = saturated_add(sum_given, ((*i).*m_res).given);
sum_max = saturated_add(sum_max, ((*i).*m_res).max);
sum_min = saturated_add(sum_min, ((*i).*m_res).min);
}
if (sum_given != (std::min)(std::max(m_resources, sum_min), sum_max))
{
std::cerr << sum_given << " " << m_resources << " " << sum_min << " " << sum_max << std::endl;
assert(false);
}
}
};
#endif
template<class It, class T>
void allocate_resources_impl(
int resources
, It start
, It end
, resource_request T::* res)
{
assert(resources >= 0);
#ifndef NDEBUG
allocate_resources_contract_check<It, T> contract_check(
resources
, start
, end
, res);
#endif
for (It i = start; i != end; ++i)
{
resource_request& r = (*i).*res;
r.leftovers = (std::max)(r.used - r.given, 0);
}
if (resources == resource_request::inf)
{
// No competition for resources.
// Just give everyone what they want.
for (It i = start; i != end; ++i)
{
((*i).*res).given = ((*i).*res).max;
}
return;
}
// Resources are scarce
int sum_max = 0;
int sum_min = 0;
// the number of consumer that saturated their
// quota last time slice
int num_saturated = 0;
// the total resources that those saturated their
// quota used. This is used to calculate the mean
// of the saturating consumers, in order to
// balance their quotas for the next time slice.
size_type saturated_sum = 0;
for (It i = start; i != end; ++i)
{
resource_request& r = (*i).*res;
sum_max = saturated_add(sum_max, r.max);
assert(r.min < resource_request::inf);
assert(r.min >= 0);
assert(r.min <= r.max);
sum_min += r.min;
// a consumer that uses 95% or more of its assigned
// quota is considered saturating
size_type used = r.used;
if (r.given == 0) continue;
if (used * 20 / r.given >= 19)
{
++num_saturated;
saturated_sum += r.given;
}
}
if (sum_max <= resources)
{
// it turns out that there's no competition for resources
// after all.
for (It i = start; i != end; ++i)
{
((*i).*res).given = ((*i).*res).max;
}
return;
}
if (sum_min >= resources)
{
// the amount of resources is smaller than
// the minimum resources to distribute, so
// give everyone the minimum
for (It i = start; i != end; ++i)
{
((*i).*res).given = ((*i).*res).min;
}
return;
}
// now, the "used" field will be used as a target value.
// the algorithm following this loop will then scale the
// used values to fit the available resources and store
// the scaled values as given. So, the ratios of the
// used values will be maintained.
for (It i = start; i != end; ++i)
{
resource_request& r = (*i).*res;
int target;
size_type used = r.used;
if (r.given > 0 && used * 20 / r.given >= 19)
{
assert(num_saturated > 0);
target = div_round_up(saturated_sum, num_saturated);
target += div_round_up(target, 10);
}
else
{
target = r.used;
}
if (target > r.max) target = r.max;
else if (target < r.min) target = r.min;
// move 12.5% towards the the target value
r.used = r.given + div_round_up(target - r.given, 8);
r.given = r.min;
}
resources = (std::max)(resources, sum_min);
int resources_to_distribute = (std::min)(resources, sum_max) - sum_min;
assert(resources_to_distribute >= 0);
#ifndef NDEBUG
int prev_resources_to_distribute = resources_to_distribute;
#endif
while (resources_to_distribute > 0)
{
// in order to scale, we need to calculate the sum of
// all the used values.
size_type total_used = 0;
size_type max_used = 0;
for (It i = start; i != end; ++i)
{
resource_request& r = (*i).*res;
if (r.given == r.max) continue;
assert(r.given < r.max);
max_used = (std::max)(max_used, (size_type)r.used + 1);
total_used += (size_type)r.used + 1;
}
size_type kNumer = resources_to_distribute;
size_type kDenom = total_used;
assert(kNumer >= 0);
assert(kDenom >= 0);
assert(kNumer <= (std::numeric_limits<int>::max)());
if (kNumer * max_used <= kDenom)
{
kNumer = 1;
kDenom = max_used;
assert(kDenom >= 0);
}
for (It i = start; i != end && resources_to_distribute > 0; ++i)
{
resource_request& r = (*i).*res;
if (r.given == r.max) continue;
assert(r.given < r.max);
size_type used = (size_type)r.used + 1;
if (used < 1) used = 1;
size_type to_give = used * kNumer / kDenom;
if (to_give > resources_to_distribute)
to_give = resources_to_distribute;
assert(to_give >= 0);
assert(to_give <= resources_to_distribute);
#ifndef NDEBUG
int tmp = resources_to_distribute;
#endif
resources_to_distribute -= give(r, (int)to_give);
assert(resources_to_distribute <= tmp);
assert(resources_to_distribute >= 0);
}
assert(resources_to_distribute >= 0);
assert(resources_to_distribute < prev_resources_to_distribute);
#ifndef NDEBUG
prev_resources_to_distribute = resources_to_distribute;
#endif
}
assert(resources_to_distribute == 0);
}
} // namespace libtorrent::aux
}
#endif

View File

@@ -273,8 +273,18 @@ namespace libtorrent
void set_max_connections(int limit); void set_max_connections(int limit);
void set_max_uploads(int limit); void set_max_uploads(int limit);
int num_uploads() const; int max_connections() const { return m_max_connections; }
int num_connections() const; int max_uploads() const { return m_max_uploads; }
int num_uploads() const { return m_num_unchoked; }
int num_connections() const
{ return m_connections.size(); }
void unchoke_peer(peer_connection& c)
{
c.send_unchoke();
++m_num_unchoked;
}
session_status status() const; session_status status() const;
void set_peer_id(peer_id const& id); void set_peer_id(peer_id const& id);
@@ -417,6 +427,28 @@ namespace libtorrent
int m_max_uploads; int m_max_uploads;
int m_max_connections; int m_max_connections;
// the number of unchoked peers
int m_num_unchoked;
// this is initialized to the unchoke_interval
// session_setting and decreased every second.
// when it reaches zero, it is reset to the
// unchoke_interval and the unchoke set is
// recomputed.
int m_unchoke_time_scaler;
// works like unchoke_time_scaler but it
// is only decresed when the unchoke set
// is recomputed, and when it reaches zero,
// the optimistic unchoke is moved to another peer.
int m_optimistic_unchoke_time_scaler;
// works like unchoke_time_scaler. Each time
// it reaches 0, and all the connections are
// used, the worst connection will be disconnected
// from the torrent with the most peers
int m_disconnect_time_scaler;
// statistics gathered from all torrents. // statistics gathered from all torrents.
stat m_stat; stat m_stat;

View File

@@ -65,7 +65,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/alert.hpp" #include "libtorrent/alert.hpp"
#include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent_handle.hpp"
#include "libtorrent/torrent.hpp" #include "libtorrent/torrent.hpp"
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/peer_request.hpp" #include "libtorrent/peer_request.hpp"
#include "libtorrent/piece_block_progress.hpp" #include "libtorrent/piece_block_progress.hpp"
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"

View File

@@ -64,7 +64,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/alert.hpp" #include "libtorrent/alert.hpp"
#include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent_handle.hpp"
#include "libtorrent/torrent.hpp" #include "libtorrent/torrent.hpp"
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/peer_request.hpp" #include "libtorrent/peer_request.hpp"
#include "libtorrent/piece_block_progress.hpp" #include "libtorrent/piece_block_progress.hpp"
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"
@@ -213,7 +212,7 @@ namespace libtorrent
void add_stat(size_type downloaded, size_type uploaded); void add_stat(size_type downloaded, size_type uploaded);
// is called once every second by the main loop // is called once every second by the main loop
void second_tick(float tick_interval); void second_tick(float tick_interval) throw();
boost::shared_ptr<socket_type> get_socket() const { return m_socket; } boost::shared_ptr<socket_type> get_socket() const { return m_socket; }
tcp::endpoint const& remote() const { return m_remote; } tcp::endpoint const& remote() const { return m_remote; }

View File

@@ -155,6 +155,13 @@ namespace libtorrent
// this is true if the peer is a seed // this is true if the peer is a seed
bool seed; bool seed;
// true if this peer currently is unchoked
// because of an optimistic unchoke.
// when the optimistic unchoke is moved to
// another peer, this peer will be choked
// if this is true
bool optimistically_unchoked;
// the time when this peer was optimistically unchoked // the time when this peer was optimistically unchoked
// the last time. // the last time.
libtorrent::ptime last_optimistically_unchoked; libtorrent::ptime last_optimistically_unchoked;
@@ -203,25 +210,18 @@ namespace libtorrent
peer_connection* connection; peer_connection* connection;
}; };
int num_peers() const int num_peers() const { return m_peers.size(); }
{
return m_peers.size();
}
int num_uploads() const
{
return m_num_unchoked;
}
typedef std::list<peer>::iterator iterator; typedef std::list<peer>::iterator iterator;
typedef std::list<peer>::const_iterator const_iterator; typedef std::list<peer>::const_iterator const_iterator;
iterator begin_peer() { return m_peers.begin(); } iterator begin_peer() { return m_peers.begin(); }
iterator end_peer() { return m_peers.end(); } iterator end_peer() { return m_peers.end(); }
bool connect_one_peer(); bool connect_one_peer();
bool disconnect_one_peer();
private: private:
/*
bool unchoke_one_peer(); bool unchoke_one_peer();
void choke_one_peer(); void choke_one_peer();
iterator find_choke_candidate(); iterator find_choke_candidate();
@@ -233,8 +233,7 @@ namespace libtorrent
void seed_choke_one_peer(); void seed_choke_one_peer();
iterator find_seed_choke_candidate(); iterator find_seed_choke_candidate();
iterator find_seed_unchoke_candidate(); iterator find_seed_unchoke_candidate();
*/
bool disconnect_one_peer();
iterator find_disconnect_candidate(); iterator find_disconnect_candidate();
iterator find_connect_candidate(); iterator find_connect_candidate();
@@ -242,10 +241,6 @@ namespace libtorrent
torrent* m_torrent; torrent* m_torrent;
// the number of unchoked peers
// at any given time
int m_num_unchoked;
// free download we have got that hasn't // free download we have got that hasn't
// been distributed yet. // been distributed yet.
size_type m_available_free_upload; size_type m_available_free_upload;
@@ -253,7 +248,7 @@ namespace libtorrent
// if there is a connection limit, // if there is a connection limit,
// we disconnect one peer every minute in hope of // we disconnect one peer every minute in hope of
// establishing a connection with a better peer // establishing a connection with a better peer
ptime m_last_optimistic_disconnect; // ptime m_last_optimistic_disconnect;
}; };
} }

View File

@@ -1,99 +0,0 @@
/*
Copyright (c) 2003, Magnus Jonsson, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_RESOURCE_REQUEST_HPP_INCLUDED
#define TORRENT_RESOURCE_REQUEST_HPP_INCLUDED
#include <boost/integer_traits.hpp>
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include "libtorrent/config.hpp"
namespace libtorrent
{
struct TORRENT_EXPORT resource_request
{
resource_request()
: used(0)
, min(0)
, max(0)
, given(0)
, leftovers(0)
{}
resource_request(int used_, int min_, int max_, int given_)
: used(used_)
, min(min_)
, max(max_)
, given(given_)
, leftovers(0)
{}
int left() const
{
assert(given <= max);
assert(given >= min);
assert(used >= 0);
return (std::max)(given - used, 0);
}
void reset() { used = leftovers; leftovers = 0; }
static const int inf = boost::integer_traits<int>::const_max;
// right now I'm actively using this amount
int used;
// given cannot be smaller than min
// and not greater than max.
int min;
int max;
// Reply: Okay, you're allowed to use this amount (a compromise):
int given;
// this is the amount of resources that exceeded the
// given limit. When the used field is reset (after resources
// have been distributed), it is reset to this number.
int leftovers;
};
}
#endif

View File

@@ -61,7 +61,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/version.hpp" #include "libtorrent/version.hpp"
#include "libtorrent/fingerprint.hpp" #include "libtorrent/fingerprint.hpp"
#include "libtorrent/resource_request.hpp"
#include "libtorrent/storage.hpp" #include "libtorrent/storage.hpp"
#ifdef _MSC_VER #ifdef _MSC_VER
@@ -266,12 +265,6 @@ namespace libtorrent
void stop_natpmp(); void stop_natpmp();
void stop_upnp(); void stop_upnp();
// Resource management used for global limits.
resource_request m_ul_bandwidth_quota;
resource_request m_dl_bandwidth_quota;
resource_request m_uploads_quota;
resource_request m_connections_quota;
private: private:
// just a way to initialize boost.filesystem // just a way to initialize boost.filesystem

View File

@@ -105,7 +105,8 @@ namespace libtorrent
, send_redundant_have(false) , send_redundant_have(false)
, lazy_bitfields(true) , lazy_bitfields(true)
, inactivity_timeout(600) , inactivity_timeout(600)
, unchoke_interval(20) , unchoke_interval(15)
, optimistic_unchoke_multiplier(4)
, num_want(200) , num_want(200)
, initial_picker_threshold(4) , initial_picker_threshold(4)
, allowed_fast_set_size(10) , allowed_fast_set_size(10)
@@ -242,6 +243,10 @@ namespace libtorrent
// the number of seconds between chokes/unchokes // the number of seconds between chokes/unchokes
int unchoke_interval; int unchoke_interval;
// the number of unchoke intervals between
// optimistic unchokes
int optimistic_unchoke_multiplier;
// if this is set, this IP will be reported do the // if this is set, this IP will be reported do the
// tracker in the ip= parameter. // tracker in the ip= parameter.
address announce_ip; address announce_ip;

View File

@@ -62,7 +62,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/tracker_manager.hpp" #include "libtorrent/tracker_manager.hpp"
#include "libtorrent/stat.hpp" #include "libtorrent/stat.hpp"
#include "libtorrent/alert.hpp" #include "libtorrent/alert.hpp"
#include "libtorrent/resource_request.hpp"
#include "libtorrent/piece_picker.hpp" #include "libtorrent/piece_picker.hpp"
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"
#include "libtorrent/escape_string.hpp" #include "libtorrent/escape_string.hpp"
@@ -154,10 +153,6 @@ namespace libtorrent
bool verify_resume_data(entry& rd, std::string& error) bool verify_resume_data(entry& rd, std::string& error)
{ assert(m_storage); return m_storage->verify_resume_data(rd, error); } { assert(m_storage); return m_storage->verify_resume_data(rd, error); }
// is called every second by session. This will
// caclulate the upload/download and number
// of connections this torrent needs. And prepare
// it for being used by allocate_resources.
void second_tick(stat& accumulator, float tick_interval); void second_tick(stat& accumulator, float tick_interval);
// debug purpose only // debug purpose only
@@ -516,11 +511,6 @@ namespace libtorrent
// -------------------------------------------- // --------------------------------------------
// RESOURCE MANAGEMENT // RESOURCE MANAGEMENT
void distribute_resources(float tick_interval);
resource_request m_uploads_quota;
resource_request m_connections_quota;
void set_peer_upload_limit(tcp::endpoint ip, int limit); void set_peer_upload_limit(tcp::endpoint ip, int limit);
void set_peer_download_limit(tcp::endpoint ip, int limit); void set_peer_download_limit(tcp::endpoint ip, int limit);
@@ -530,7 +520,9 @@ namespace libtorrent
int download_limit() const; int download_limit() const;
void set_max_uploads(int limit); void set_max_uploads(int limit);
int max_uploads() const { return m_max_uploads; }
void set_max_connections(int limit); void set_max_connections(int limit);
int max_connections() const { return m_max_connections; }
void move_storage(fs::path const& save_path); void move_storage(fs::path const& save_path);
// unless this returns true, new connections must wait // unless this returns true, new connections must wait
@@ -705,9 +697,9 @@ namespace libtorrent
// determine the timeout until next try. // determine the timeout until next try.
int m_failed_trackers; int m_failed_trackers;
// this is a counter that is increased every // this is a counter that is decreased every
// second, and when it reaches 10, the policy::pulse() // second, and when it reaches 0, the policy::pulse()
// is called and the time scaler is reset to 0. // is called and the time scaler is reset to 10.
int m_time_scaler; int m_time_scaler;
// the bitmask that says which pieces we have // the bitmask that says which pieces we have
@@ -774,6 +766,12 @@ namespace libtorrent
session_settings const& m_settings; session_settings const& m_settings;
storage_constructor_type m_storage_constructor; storage_constructor_type m_storage_constructor;
// the maximum number of uploads for this torrent
int m_max_uploads;
// the maximum number of connections for this torrent
int m_max_connections;
#ifndef TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_DISABLE_EXTENSIONS
typedef std::list<boost::shared_ptr<torrent_plugin> > extension_list_t; typedef std::list<boost::shared_ptr<torrent_plugin> > extension_list_t;

View File

@@ -65,7 +65,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/alert.hpp" #include "libtorrent/alert.hpp"
#include "libtorrent/torrent_handle.hpp" #include "libtorrent/torrent_handle.hpp"
#include "libtorrent/torrent.hpp" #include "libtorrent/torrent.hpp"
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/peer_request.hpp" #include "libtorrent/peer_request.hpp"
#include "libtorrent/piece_block_progress.hpp" #include "libtorrent/piece_block_progress.hpp"
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"

View File

@@ -1,225 +0,0 @@
/*
Copyright (c) 2006, Magnus Jonsson, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
//The Standard Library defines the two template functions std::min()
//and std::max() in the <algorithm> header. In general, you should
//use these template functions for calculating the min and max values
//of a pair. Unfortunately, Visual C++ does not define these function
// templates. This is because the names min and max clash with
//the traditional min and max macros defined in <windows.h>.
//As a workaround, Visual C++ defines two alternative templates with
//identical functionality called _cpp_min() and _cpp_max(). You can
//use them instead of std::min() and std::max().To disable the
//generation of the min and max macros in Visual C++, #define
//NOMINMAX before #including <windows.h>.
#include "libtorrent/pch.hpp"
#ifdef _WIN32
//support boost1.32.0(2004-11-19 18:47)
//now all libs can be compiled and linked with static module
#define NOMINMAX
#endif
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/size_type.hpp"
#include "libtorrent/peer_connection.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/aux_/allocate_resources_impl.hpp"
#include <cassert>
#include <algorithm>
#include <boost/limits.hpp>
#if defined(_MSC_VER) && _MSC_VER < 1310
#define for if (false) {} else for
#else
#include <boost/iterator/transform_iterator.hpp>
#endif
namespace libtorrent
{
int saturated_add(int a, int b)
{
assert(a >= 0);
assert(b >= 0);
assert(a <= resource_request::inf);
assert(b <= resource_request::inf);
assert(resource_request::inf + resource_request::inf < 0);
unsigned int sum = unsigned(a) + unsigned(b);
if (sum > unsigned(resource_request::inf))
sum = resource_request::inf;
assert(sum >= unsigned(a) && sum >= unsigned(b));
return int(sum);
}
#if defined(_MSC_VER) && _MSC_VER < 1310
namespace detail
{
struct iterator_wrapper
{
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
orig_iter iter;
iterator_wrapper(orig_iter i): iter(i) {}
void operator++() { ++iter; }
torrent& operator*() { return *(iter->second); }
bool operator==(const iterator_wrapper& i) const
{ return iter == i.iter; }
bool operator!=(const iterator_wrapper& i) const
{ return iter != i.iter; }
};
struct iterator_wrapper2
{
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
orig_iter iter;
iterator_wrapper2(orig_iter i): iter(i) {}
void operator++() { ++iter; }
peer_connection& operator*() { return *(iter->second); }
bool operator==(const iterator_wrapper2& i) const
{ return iter == i.iter; }
bool operator!=(const iterator_wrapper2& i) const
{ return iter != i.iter; }
};
}
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
, resource_request torrent::* res)
{
aux::allocate_resources_impl(
resources
, detail::iterator_wrapper(c.begin())
, detail::iterator_wrapper(c.end())
, res);
}
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
aux::allocate_resources_impl(
resources
, detail::iterator_wrapper2(c.begin())
, detail::iterator_wrapper2(c.end())
, res);
}
#else
namespace aux
{
peer_connection& pick_peer(
std::pair<boost::shared_ptr<stream_socket>
, boost::intrusive_ptr<peer_connection> > const& p)
{
return *p.second;
}
peer_connection& pick_peer2(
std::pair<tcp::endpoint, peer_connection*> const& p)
{
return *p.second;
}
torrent& deref(std::pair<sha1_hash, boost::shared_ptr<torrent> > const& p)
{
return *p.second;
}
session& deref(session* p)
{
return *p;
}
}
void allocate_resources(
int resources
, std::map<sha1_hash, boost::shared_ptr<torrent> >& c
, resource_request torrent::* res)
{
typedef std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator orig_iter;
typedef std::pair<sha1_hash, boost::shared_ptr<torrent> > in_param;
typedef boost::transform_iterator<torrent& (*)(in_param const&), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(c.begin(), &aux::deref)
, new_iter(c.end(), &aux::deref)
, res);
}
void allocate_resources(
int resources
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
typedef std::pair<tcp::endpoint, peer_connection*> in_param;
typedef boost::transform_iterator<peer_connection& (*)(in_param const&), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(c.begin(), &aux::pick_peer2)
, new_iter(c.end(), &aux::pick_peer2)
, res);
}
void allocate_resources(
int resources
, std::vector<session*>& _sessions
, resource_request session::* res)
{
typedef std::vector<session*>::iterator orig_iter;
typedef session* in_param;
typedef boost::transform_iterator<session& (*)(in_param), orig_iter> new_iter;
aux::allocate_resources_impl(
resources
, new_iter(_sessions.begin(), &aux::deref)
, new_iter(_sessions.end(), &aux::deref)
, res);
}
#endif
} // namespace libtorrent

View File

@@ -109,8 +109,8 @@ namespace libtorrent
, m_prefer_whole_pieces(false) , m_prefer_whole_pieces(false)
, m_request_large_blocks(false) , m_request_large_blocks(false)
, m_non_prioritized(false) , m_non_prioritized(false)
, m_upload_limit(resource_request::inf) , m_upload_limit(bandwidth_limit::inf)
, m_download_limit(resource_request::inf) , m_download_limit(bandwidth_limit::inf)
, m_peer_info(peerinfo) , m_peer_info(peerinfo)
, m_speed(slow) , m_speed(slow)
, m_connection_ticket(-1) , m_connection_ticket(-1)
@@ -185,8 +185,8 @@ namespace libtorrent
, m_prefer_whole_pieces(false) , m_prefer_whole_pieces(false)
, m_request_large_blocks(false) , m_request_large_blocks(false)
, m_non_prioritized(false) , m_non_prioritized(false)
, m_upload_limit(resource_request::inf) , m_upload_limit(bandwidth_limit::inf)
, m_download_limit(resource_request::inf) , m_download_limit(bandwidth_limit::inf)
, m_peer_info(peerinfo) , m_peer_info(peerinfo)
, m_speed(slow) , m_speed(slow)
, m_remote_bytes_dled(0) , m_remote_bytes_dled(0)
@@ -526,30 +526,18 @@ namespace libtorrent
&& p.start + p.length <= t->torrent_file().piece_size(p.piece) && p.start + p.length <= t->torrent_file().piece_size(p.piece)
&& (p.start % t->block_size() == 0); && (p.start % t->block_size() == 0);
} }
struct disconnect_torrent
{
disconnect_torrent(boost::weak_ptr<torrent>& t): m_t(&t) {}
~disconnect_torrent() { if (m_t) m_t->reset(); }
void cancel() { m_t = 0; }
private:
boost::weak_ptr<torrent>* m_t;
};
void peer_connection::attach_to_torrent(sha1_hash const& ih) void peer_connection::attach_to_torrent(sha1_hash const& ih)
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
assert(!m_disconnecting); assert(!m_disconnecting);
m_torrent = m_ses.find_torrent(ih); assert(m_torrent.expired());
boost::weak_ptr<torrent> wpt = m_ses.find_torrent(ih);
boost::shared_ptr<torrent> t = m_torrent.lock(); boost::shared_ptr<torrent> t = m_torrent.lock();
if (t && t->is_aborted()) if (t && t->is_aborted())
{
m_torrent.reset();
t.reset(); t.reset();
}
if (!t) if (!t)
{ {
@@ -560,7 +548,6 @@ namespace libtorrent
throw std::runtime_error("got info-hash that is not in our session"); throw std::runtime_error("got info-hash that is not in our session");
} }
disconnect_torrent disconnect(m_torrent);
if (t->is_paused()) if (t->is_paused())
{ {
// paused torrents will not accept // paused torrents will not accept
@@ -571,21 +558,27 @@ namespace libtorrent
throw std::runtime_error("connection rejected by paused torrent"); throw std::runtime_error("connection rejected by paused torrent");
} }
assert(m_torrent.expired());
// check to make sure we don't have another connection with the same // check to make sure we don't have another connection with the same
// info_hash and peer_id. If we do. close this connection. // info_hash and peer_id. If we do. close this connection.
t->attach_peer(this); t->attach_peer(this);
m_torrent = wpt;
assert(!m_torrent.expired());
// if the torrent isn't ready to accept // if the torrent isn't ready to accept
// connections yet, we'll have to wait with // connections yet, we'll have to wait with
// our initialization // our initialization
if (t->ready_for_connections()) init(); if (t->ready_for_connections()) init();
assert(!m_torrent.expired());
// assume the other end has no pieces // assume the other end has no pieces
// if we don't have valid metadata yet, // if we don't have valid metadata yet,
// leave the vector unallocated // leave the vector unallocated
assert(m_num_pieces == 0); assert(m_num_pieces == 0);
std::fill(m_have_piece.begin(), m_have_piece.end(), false); std::fill(m_have_piece.begin(), m_have_piece.end(), false);
disconnect.cancel(); assert(!m_torrent.expired());
} }
// message handlers // message handlers
@@ -1543,7 +1536,7 @@ namespace libtorrent
// if the peer has the piece and we want // if the peer has the piece and we want
// to download it, request it // to download it, request it
if (m_have_piece.size() > index if (int(m_have_piece.size()) > index
&& m_have_piece[index] && m_have_piece[index]
&& t->has_picker() && t->has_picker()
&& t->picker().piece_priority(index) > 0) && t->picker().piece_priority(index) > 0)
@@ -1900,7 +1893,7 @@ namespace libtorrent
void peer_connection::set_upload_limit(int limit) void peer_connection::set_upload_limit(int limit)
{ {
assert(limit >= -1); assert(limit >= -1);
if (limit == -1) limit = resource_request::inf; if (limit == -1) limit = std::numeric_limits<int>::max();
if (limit < 10) limit = 10; if (limit < 10) limit = 10;
m_upload_limit = limit; m_upload_limit = limit;
m_bandwidth_limit[upload_channel].throttle(m_upload_limit); m_bandwidth_limit[upload_channel].throttle(m_upload_limit);
@@ -1909,7 +1902,7 @@ namespace libtorrent
void peer_connection::set_download_limit(int limit) void peer_connection::set_download_limit(int limit)
{ {
assert(limit >= -1); assert(limit >= -1);
if (limit == -1) limit = resource_request::inf; if (limit == -1) limit = std::numeric_limits<int>::max();
if (limit < 10) limit = 10; if (limit < 10) limit = 10;
m_download_limit = limit; m_download_limit = limit;
m_bandwidth_limit[download_channel].throttle(m_download_limit); m_bandwidth_limit[download_channel].throttle(m_download_limit);
@@ -2044,10 +2037,13 @@ namespace libtorrent
if (m_packet_size >= m_recv_pos) m_recv_buffer.resize(m_packet_size); if (m_packet_size >= m_recv_pos) m_recv_buffer.resize(m_packet_size);
} }
void peer_connection::second_tick(float tick_interval) void peer_connection::second_tick(float tick_interval) throw()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
try
{
ptime now(time_now()); ptime now(time_now());
boost::shared_ptr<torrent> t = m_torrent.lock(); boost::shared_ptr<torrent> t = m_torrent.lock();
@@ -2186,43 +2182,14 @@ namespace libtorrent
} }
fill_send_buffer(); fill_send_buffer();
/*
size_type diff = share_diff();
enum { block_limit = 2 }; // how many blocks difference is considered unfair
// if the peer has been choked, send the current piece
// as fast as possible
if (diff > block_limit*m_torrent->block_size() || m_torrent->is_seed() || is_choked())
{
// if we have downloaded more than one piece more
// than we have uploaded OR if we are a seed
// have an unlimited upload rate
m_ul_bandwidth_quota.wanted = std::numeric_limits<int>::max();
} }
else catch (std::exception& e)
{ {
float ratio = m_torrent->ratio(); #ifdef TORRENT_VERBOSE_LOGGING
// if we have downloaded too much, response with an (*m_logger) << "**ERROR**: " << e.what() << "\n";
// upload rate of 10 kB/s more than we dowlload #endif
// if we have uploaded too much, send with a rate of m_ses.connection_failed(m_socket, remote(), e.what());
// 10 kB/s less than we receive
int bias = 0;
if (diff > -block_limit*m_torrent->block_size())
{
bias = static_cast<int>(m_statistics.download_rate() * ratio) / 2;
if (bias < 10*1024) bias = 10*1024;
}
else
{
bias = -static_cast<int>(m_statistics.download_rate() * ratio) / 2;
}
m_ul_bandwidth_quota.wanted = static_cast<int>(m_statistics.download_rate()) + bias;
// the maximum send_quota given our download rate from this peer
if (m_ul_bandwidth_quota.wanted < 256) m_ul_bandwidth_quota.wanted = 256;
} }
*/
} }
void peer_connection::fill_send_buffer() void peer_connection::fill_send_buffer()

View File

@@ -331,9 +331,8 @@ namespace libtorrent
policy::policy(torrent* t) policy::policy(torrent* t)
: m_torrent(t) : m_torrent(t)
, m_num_unchoked(0)
, m_available_free_upload(0) , m_available_free_upload(0)
, m_last_optimistic_disconnect(min_time()) // , m_last_optimistic_disconnect(min_time())
{ assert(t); } { assert(t); }
// disconnects and removes all peers that are now filtered // disconnects and removes all peers that are now filtered
@@ -375,7 +374,7 @@ namespace libtorrent
m_peers.erase(i++); m_peers.erase(i++);
} }
} }
/*
// finds the peer that has the worst download rate // finds the peer that has the worst download rate
// and returns it. May return 0 if all peers are // and returns it. May return 0 if all peers are
// choked. // choked.
@@ -457,7 +456,7 @@ namespace libtorrent
} }
return unchoke_peer; return unchoke_peer;
} }
*/
policy::iterator policy::find_disconnect_candidate() policy::iterator policy::find_disconnect_candidate()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@@ -542,7 +541,7 @@ namespace libtorrent
return candidate; return candidate;
} }
/*
policy::iterator policy::find_seed_choke_candidate() policy::iterator policy::find_seed_choke_candidate()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@@ -648,7 +647,7 @@ namespace libtorrent
--m_num_unchoked; --m_num_unchoked;
} }
} }
*/
void policy::pulse() void policy::pulse()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@@ -680,7 +679,7 @@ namespace libtorrent
// ------------------------------------- // -------------------------------------
// maintain the number of connections // maintain the number of connections
// ------------------------------------- // -------------------------------------
/*
// count the number of connected peers except for peers // count the number of connected peers except for peers
// that are currently in the process of disconnecting // that are currently in the process of disconnecting
int num_connected_peers = 0; int num_connected_peers = 0;
@@ -692,10 +691,9 @@ namespace libtorrent
++num_connected_peers; ++num_connected_peers;
} }
if (m_torrent->m_connections_quota.given != std::numeric_limits<int>::max()) if (m_torrent->max_connections() != std::numeric_limits<int>::max())
{ {
int max_connections = m_torrent->max_connections();
int max_connections = m_torrent->m_connections_quota.given;
if (num_connected_peers >= max_connections) if (num_connected_peers >= max_connections)
{ {
@@ -723,7 +721,7 @@ namespace libtorrent
--num_connected_peers; --num_connected_peers;
} }
} }
*/
// ------------------------ // ------------------------
// upload shift // upload shift
// ------------------------ // ------------------------
@@ -754,7 +752,7 @@ namespace libtorrent
, m_torrent->end() , m_torrent->end()
, m_available_free_upload); , m_available_free_upload);
} }
/*
// ------------------------ // ------------------------
// seed choking policy // seed choking policy
// ------------------------ // ------------------------
@@ -870,6 +868,7 @@ namespace libtorrent
while (m_num_unchoked < m_torrent->m_uploads_quota.given while (m_num_unchoked < m_torrent->m_uploads_quota.given
&& unchoke_one_peer()); && unchoke_one_peer());
} }
*/
} }
int policy::count_choked() const int policy::count_choked() const
@@ -902,7 +901,8 @@ namespace libtorrent
// override at a time // override at a time
assert(c.remote() == c.get_socket()->remote_endpoint()); assert(c.remote() == c.get_socket()->remote_endpoint());
if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given if (m_torrent->num_peers() >= m_torrent->max_connections()
&& m_torrent->session().num_connections() >= m_torrent->session().max_connections()
&& c.remote().address() != m_torrent->current_tracker().address()) && c.remote().address() != m_torrent->current_tracker().address())
{ {
throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect
@@ -984,7 +984,7 @@ namespace libtorrent
i->connection = &c; i->connection = &c;
assert(i->connection); assert(i->connection);
i->connected = time_now(); i->connected = time_now();
m_last_optimistic_disconnect = time_now(); // m_last_optimistic_disconnect = time_now();
} }
void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid
@@ -1172,14 +1172,38 @@ namespace libtorrent
// In that case we don't care if people are leeching, they // In that case we don't care if people are leeching, they
// can't pay for their downloads anyway. // can't pay for their downloads anyway.
if (c.is_choked() if (c.is_choked()
&& m_num_unchoked < m_torrent->m_uploads_quota.given && m_torrent->session().num_uploads() < m_torrent->session().max_uploads()
&& (m_torrent->ratio() == 0 && (m_torrent->ratio() == 0
|| c.share_diff() >= -free_upload_amount || c.share_diff() >= -free_upload_amount
|| m_torrent->is_seed())) || m_torrent->is_seed()))
{ {
c.send_unchoke(); m_torrent->session().unchoke_peer(c);
++m_num_unchoked;
} }
#if defined(TORRENT_VERBOSE_LOGGING)
else if (c.is_choked())
{
std::string reason;
if (m_torrent->session().num_uploads() >= m_torrent->session().max_uploads())
{
reason = "the number of uploads ("
+ boost::lexical_cast<std::string>(m_torrent->session().num_uploads())
+ ") is more than or equal to the limit ("
+ boost::lexical_cast<std::string>(m_torrent->session().max_uploads())
+ ")";
}
else
{
reason = "the share ratio ("
+ boost::lexical_cast<std::string>(c.share_diff())
+ ") is <= free_upload_amount ("
+ boost::lexical_cast<std::string>(int(free_upload_amount))
+ ") and we are not seeding and the ratio ("
+ boost::lexical_cast<std::string>(m_torrent->ratio())
+ ")is non-zero";
}
(*c.m_logger) << time_now_string() << " DID NOT UNCHOKE [ " << reason << " ]\n";
}
#endif
} }
// called when a peer is no longer interested in us // called when a peer is no longer interested in us
@@ -1211,7 +1235,7 @@ namespace libtorrent
} }
*/ */
} }
/*
bool policy::unchoke_one_peer() bool policy::unchoke_one_peer()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@@ -1240,7 +1264,7 @@ namespace libtorrent
p->connection->send_choke(); p->connection->send_choke();
--m_num_unchoked; --m_num_unchoked;
} }
*/
bool policy::connect_one_peer() bool policy::connect_one_peer()
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@@ -1256,7 +1280,7 @@ namespace libtorrent
try try
{ {
p->connected = m_last_optimistic_disconnect = time_now(); p->connected = time_now();
p->connection = m_torrent->connect_to_peer(&*p); p->connection = m_torrent->connect_to_peer(&*p);
if (p->connection == 0) return false; if (p->connection == 0) return false;
p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload); p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload);
@@ -1296,6 +1320,7 @@ namespace libtorrent
// assert(c.is_disconnecting()); // assert(c.is_disconnecting());
bool unchoked = false; bool unchoked = false;
#warning extract policy::peer pointer from c
iterator i = std::find_if( iterator i = std::find_if(
m_peers.begin() m_peers.begin()
, m_peers.end() , m_peers.end()
@@ -1305,6 +1330,7 @@ namespace libtorrent
if (i == m_peers.end()) return; if (i == m_peers.end()) return;
assert(i->connection == &c); assert(i->connection == &c);
i->connection = 0; i->connection = 0;
i->optimistically_unchoked = false;
i->connected = time_now(); i->connected = time_now();
if (!c.is_choked() && !m_torrent->is_aborted()) if (!c.is_choked() && !m_torrent->is_aborted())
@@ -1330,15 +1356,15 @@ namespace libtorrent
i->prev_amount_download += c.statistics().total_payload_download(); i->prev_amount_download += c.statistics().total_payload_download();
i->prev_amount_upload += c.statistics().total_payload_upload(); i->prev_amount_upload += c.statistics().total_payload_upload();
if (unchoked) // if (unchoked)
{ // {
// if the peer that is diconnecting is unchoked // if the peer that is diconnecting is unchoked
// then unchoke another peer in order to maintain // then unchoke another peer in order to maintain
// the total number of unchoked peers // the total number of unchoked peers
--m_num_unchoked; // --m_num_unchoked;
if (m_torrent->is_seed()) seed_unchoke_one_peer(); // if (m_torrent->is_seed()) seed_unchoke_one_peer();
else unchoke_one_peer(); // else unchoke_one_peer();
} // }
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@@ -1376,7 +1402,6 @@ namespace libtorrent
void policy::check_invariant() const void policy::check_invariant() const
{ {
if (m_torrent->is_aborted()) return; if (m_torrent->is_aborted()) return;
int actual_unchoked = 0;
int connected_peers = 0; int connected_peers = 0;
int total_connections = 0; int total_connections = 0;
@@ -1405,10 +1430,7 @@ namespace libtorrent
++nonempty_connections; ++nonempty_connections;
if (!p.connection->is_disconnecting()) if (!p.connection->is_disconnecting())
++connected_peers; ++connected_peers;
if (!p.connection->is_choked()) ++actual_unchoked;
} }
// assert(actual_unchoked <= m_torrent->m_uploads_quota.given);
assert(actual_unchoked == m_num_unchoked);
int num_torrent_peers = 0; int num_torrent_peers = 0;
for (torrent::const_peer_iterator i = m_torrent->begin(); for (torrent::const_peer_iterator i = m_torrent->begin();
@@ -1475,6 +1497,7 @@ namespace libtorrent
, failcount(0) , failcount(0)
, hashfails(0) , hashfails(0)
, seed(false) , seed(false)
, optimistically_unchoked(false)
, last_optimistically_unchoked(min_time()) , last_optimistically_unchoked(min_time())
, connected(min_time()) , connected(min_time())
, trust_points(0) , trust_points(0)

View File

@@ -68,7 +68,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/alert_types.hpp" #include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp" #include "libtorrent/invariant_check.hpp"
#include "libtorrent/file.hpp" #include "libtorrent/file.hpp"
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/ip_filter.hpp" #include "libtorrent/ip_filter.hpp"
#include "libtorrent/socket.hpp" #include "libtorrent/socket.hpp"

View File

@@ -68,7 +68,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/alert_types.hpp" #include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp" #include "libtorrent/invariant_check.hpp"
#include "libtorrent/file.hpp" #include "libtorrent/file.hpp"
#include "libtorrent/allocate_resources.hpp"
#include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/ip_filter.hpp" #include "libtorrent/ip_filter.hpp"
#include "libtorrent/socket.hpp" #include "libtorrent/socket.hpp"
@@ -505,8 +504,12 @@ namespace detail
, m_listen_interface(address::from_string(listen_interface), listen_port_range.first) , m_listen_interface(address::from_string(listen_interface), listen_port_range.first)
, m_external_listen_port(0) , m_external_listen_port(0)
, m_abort(false) , m_abort(false)
, m_max_uploads(-1) , m_max_uploads(8)
, m_max_connections(-1) , m_max_connections(200)
, m_num_unchoked(0)
, m_unchoke_time_scaler(0)
, m_optimistic_unchoke_time_scaler(0)
, m_disconnect_time_scaler(0)
, m_incoming_connection(false) , m_incoming_connection(false)
, m_last_tick(time_now()) , m_last_tick(time_now())
#ifndef TORRENT_DISABLE_DHT #ifndef TORRENT_DISABLE_DHT
@@ -821,7 +824,10 @@ namespace detail
assert(p->is_disconnecting()); assert(p->is_disconnecting());
connection_map::iterator i = m_connections.find(p->get_socket()); connection_map::iterator i = m_connections.find(p->get_socket());
if (i != m_connections.end()) if (i != m_connections.end())
{
if (!i->second->is_choked()) --m_num_unchoked;
m_connections.erase(i); m_connections.erase(i);
}
} }
void session_impl::set_peer_id(peer_id const& id) void session_impl::set_peer_id(peer_id const& id)
@@ -901,7 +907,9 @@ namespace detail
// round robin fashion, so that every torrent is // round robin fashion, so that every torrent is
// equallt likely to connect to a peer // equallt likely to connect to a peer
if (!m_torrents.empty() && m_half_open.free_slots()) if (!m_torrents.empty()
&& m_half_open.free_slots()
&& num_connections() < m_max_connections)
{ {
// this is the maximum number of connections we will // this is the maximum number of connections we will
// attempt this tick // attempt this tick
@@ -918,11 +926,13 @@ namespace detail
{ {
torrent& t = *i->second; torrent& t = *i->second;
if (t.want_more_peers()) if (t.want_more_peers())
{
if (t.try_connect_peer()) if (t.try_connect_peer())
{ {
--max_connections; --max_connections;
steps_since_last_connect = 0; steps_since_last_connect = 0;
} }
}
++m_next_connect_torrent; ++m_next_connect_torrent;
++steps_since_last_connect; ++steps_since_last_connect;
++i; ++i;
@@ -976,18 +986,7 @@ namespace detail
continue; continue;
} }
try c.keep_alive();
{
c.keep_alive();
}
catch (std::exception& exc)
{
#ifdef TORRENT_VERBOSE_LOGGING
(*c.m_logger) << "**ERROR**: " << exc.what() << "\n";
#endif
c.set_failed();
c.disconnect();
}
} }
// check each torrent for tracker updates // check each torrent for tracker updates
@@ -1019,30 +1018,148 @@ namespace detail
} }
m_stat.second_tick(tick_interval); m_stat.second_tick(tick_interval);
// distribute the maximum upload rate among the torrents
assert(m_max_uploads >= -1); // --------------------------------------------------------------
assert(m_max_connections >= -1); // unchoke set and optimistic unchoke calculations
// --------------------------------------------------------------
allocate_resources(m_max_uploads == -1 m_unchoke_time_scaler--;
? std::numeric_limits<int>::max() if (m_unchoke_time_scaler <= 0 && !m_connections.empty())
: m_max_uploads
, m_torrents
, &torrent::m_uploads_quota);
allocate_resources(m_max_connections == -1
? std::numeric_limits<int>::max()
: m_max_connections
, m_torrents
, &torrent::m_connections_quota);
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
= m_torrents.begin(); i != m_torrents.end(); ++i)
{ {
#ifndef NDEBUG m_unchoke_time_scaler = settings().unchoke_interval;
i->second->check_invariant();
#endif std::vector<peer_connection*> peers;
i->second->distribute_resources(tick_interval); for (connection_map::iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
{
peer_connection* p = i->second.get();
torrent* t = p->associated_torrent().lock().get();
if (!p->peer_info_struct()
|| !p->is_peer_interested()
|| p->is_disconnecting()
|| p->is_connecting()
|| (p->share_diff() < -free_upload_amount
&& t && !t->is_seed()))
{
if (!i->second->is_choked())
i->second->send_choke();
continue;
}
peers.push_back(i->second.get());
}
// sort the peers that are eligible for unchoke by download rate and secondary
// by total upload. The reason for this is, if all torrents are being seeded,
// the download rate will be 0, and the peers we have sent the least to should
// be unchoked
std::sort(peers.begin(), peers.end()
, bind(&stat::total_payload_upload, bind(&peer_connection::statistics, _1))
< bind(&stat::total_payload_upload, bind(&peer_connection::statistics, _2)));
std::stable_sort(peers.begin(), peers.end()
, bind(&stat::download_payload_rate, bind(&peer_connection::statistics, _1))
> bind(&stat::download_payload_rate, bind(&peer_connection::statistics, _2)));
// reserve one upload slot for optimistic unchokes
int unchoke_set_size = m_max_uploads - 1;
m_num_unchoked = 0;
// go through all the peers and unchoke the first ones and choke
// all the other ones.
for (std::vector<peer_connection*>::iterator i = peers.begin()
, end(peers.end()); i != end; ++i)
{
peer_connection* p = *i;
assert(p);
if (unchoke_set_size > 0)
{
if (p->is_choked()) p->send_unchoke();
assert(p->peer_info_struct());
if (p->peer_info_struct()->optimistically_unchoked)
{
// force a new optimistic unchoke
m_optimistic_unchoke_time_scaler = 0;
p->peer_info_struct()->optimistically_unchoked = false;
}
--unchoke_set_size;
++m_num_unchoked;
}
else
{
if (!p->is_choked() && !p->peer_info_struct()->optimistically_unchoked)
p->send_choke();
}
}
m_optimistic_unchoke_time_scaler--;
if (m_optimistic_unchoke_time_scaler <= 0)
{
m_optimistic_unchoke_time_scaler
= settings().optimistic_unchoke_multiplier;
// find the peer that has been waiting the longest to be optimistically
// unchoked
connection_map::iterator current_optimistic_unchoke = m_connections.end();
connection_map::iterator optimistic_unchoke_candidate = m_connections.end();
ptime last_unchoke = max_time();
for (connection_map::iterator i = m_connections.begin()
, end(m_connections.end()); i != end; ++i)
{
peer_connection* p = i->second.get();
assert(p);
policy::peer* pi = p->peer_info_struct();
if (!pi) continue;
if (pi->optimistically_unchoked)
{
assert(current_optimistic_unchoke == m_connections.end());
current_optimistic_unchoke = i;
}
if (pi->last_optimistically_unchoked < last_unchoke
&& !p->is_connecting()
&& !p->is_disconnecting()
&& p->is_peer_interested())
{
last_unchoke = pi->last_optimistically_unchoked;
optimistic_unchoke_candidate = i;
}
}
if (optimistic_unchoke_candidate != m_connections.end()
&& optimistic_unchoke_candidate != current_optimistic_unchoke)
{
if (current_optimistic_unchoke != m_connections.end())
{
current_optimistic_unchoke->second->send_choke();
current_optimistic_unchoke->second->peer_info_struct()->optimistically_unchoked = false;
}
optimistic_unchoke_candidate->second->send_unchoke();
optimistic_unchoke_candidate->second->peer_info_struct()->optimistically_unchoked = true;
}
if (optimistic_unchoke_candidate != m_connections.end())
++m_num_unchoked;
}
}
// --------------------------------------------------------------
// disconnect peers when we have too many
// --------------------------------------------------------------
--m_disconnect_time_scaler;
if (m_disconnect_time_scaler <= 0)
{
m_disconnect_time_scaler = 60;
// every 60 seconds, disconnect the worst peer
// if we have reached the connection limit
if (num_connections() >= max_connections() && !m_torrents.empty())
{
torrent_map::iterator i = std::max_element(m_torrents.begin(), m_torrents.end()
, bind(&torrent::num_peers, bind(&torrent_map::value_type::second, _1)));
assert(i != m_torrents.end());
i->second->get_policy().disconnect_one_peer();
}
} }
} }
catch (std::exception& exc) catch (std::exception& exc)
@@ -1866,24 +1983,6 @@ namespace detail
m_bandwidth_manager[peer_connection::upload_channel]->throttle(bytes_per_second); m_bandwidth_manager[peer_connection::upload_channel]->throttle(bytes_per_second);
} }
int session_impl::num_uploads() const
{
int uploads = 0;
mutex_t::scoped_lock l(m_mutex);
for (torrent_map::const_iterator i = m_torrents.begin()
, end(m_torrents.end()); i != end; i++)
{
uploads += i->second->get_policy().num_uploads();
}
return uploads;
}
int session_impl::num_connections() const
{
mutex_t::scoped_lock l(m_mutex);
return m_connections.size();
}
std::auto_ptr<alert> session_impl::pop_alert() std::auto_ptr<alert> session_impl::pop_alert()
{ {
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
@@ -1978,17 +2077,25 @@ namespace detail
void session_impl::check_invariant(const char *place) void session_impl::check_invariant(const char *place)
{ {
assert(place); assert(place);
int unchokes = 0;
int num_optimistic = 0;
for (connection_map::iterator i = m_connections.begin(); for (connection_map::iterator i = m_connections.begin();
i != m_connections.end(); ++i) i != m_connections.end(); ++i)
{ {
assert(i->second); assert(i->second);
boost::shared_ptr<torrent> t = i->second->associated_torrent().lock(); boost::shared_ptr<torrent> t = i->second->associated_torrent().lock();
if (!i->second->is_choked()) ++unchokes;
if (i->second->peer_info_struct()
&& i->second->peer_info_struct()->optimistically_unchoked)
++num_optimistic;
if (t) if (t)
{ {
assert(t->get_policy().has_connection(boost::get_pointer(i->second))); assert(t->get_policy().has_connection(boost::get_pointer(i->second)));
} }
} }
assert(num_optimistic == 0 || num_optimistic == 1);
assert(m_num_unchoked == unchokes);
} }
#endif #endif

View File

@@ -199,18 +199,12 @@ namespace libtorrent
, m_connections_initialized(true) , m_connections_initialized(true)
, m_settings(s) , m_settings(s)
, m_storage_constructor(sc) , m_storage_constructor(sc)
, m_max_uploads(std::numeric_limits<int>::max())
, m_max_connections(std::numeric_limits<int>::max())
{ {
#ifndef NDEBUG #ifndef NDEBUG
m_initial_done = 0; m_initial_done = 0;
#endif #endif
m_uploads_quota.min = 0;
m_connections_quota.min = 2;
// this will be corrected the next time the main session
// distributes resources, i.e. on average in 0.5 seconds
m_connections_quota.given = 100;
m_uploads_quota.max = std::numeric_limits<int>::max();
m_connections_quota.max = std::numeric_limits<int>::max();
m_policy.reset(new policy(this)); m_policy.reset(new policy(this));
} }
@@ -277,13 +271,6 @@ namespace libtorrent
if (name) m_name.reset(new std::string(name)); if (name) m_name.reset(new std::string(name));
m_uploads_quota.min = 0;
m_connections_quota.min = 2;
// this will be corrected the next time the main session
// distributes resources, i.e. on average in 0.5 seconds
m_connections_quota.given = 100;
m_uploads_quota.max = std::numeric_limits<int>::max();
m_connections_quota.max = std::numeric_limits<int>::max();
if (tracker_url) if (tracker_url)
{ {
m_trackers.push_back(announce_entry(tracker_url)); m_trackers.push_back(announce_entry(tracker_url));
@@ -329,6 +316,14 @@ namespace libtorrent
INVARIANT_CHECK; INVARIANT_CHECK;
#if defined(TORRENT_VERBOSE_LOGGING)
for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++i)
{
(*i->second->m_logger) << "*** DESTRUCTING TORRENT\n";
}
#endif
assert(m_abort); assert(m_abort);
if (!m_connections.empty()) if (!m_connections.empty())
disconnect_all(); disconnect_all();
@@ -1020,6 +1015,15 @@ namespace libtorrent
m_event = tracker_request::stopped; m_event = tracker_request::stopped;
// disconnect all peers and close all // disconnect all peers and close all
// files belonging to the torrents // files belonging to the torrents
#if defined(TORRENT_VERBOSE_LOGGING)
for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++i)
{
(*i->second->m_logger) << "*** ABORTING TORRENT\n";
}
#endif
disconnect_all(); disconnect_all();
if (m_owning_storage.get()) m_storage->async_release_files(); if (m_owning_storage.get()) m_storage->async_release_files();
m_owning_storage = 0; m_owning_storage = 0;
@@ -1890,7 +1894,7 @@ namespace libtorrent
void torrent::attach_peer(peer_connection* p) void torrent::attach_peer(peer_connection* p)
{ {
INVARIANT_CHECK; // INVARIANT_CHECK;
assert(p != 0); assert(p != 0);
assert(!p->is_local()); assert(!p->is_local());
@@ -1955,7 +1959,7 @@ namespace libtorrent
bool torrent::want_more_peers() const bool torrent::want_more_peers() const
{ {
return int(m_connections.size()) < m_connections_quota.given return int(m_connections.size()) < m_max_connections
&& m_ses.m_half_open.free_slots() && m_ses.m_half_open.free_slots()
&& !m_paused; && !m_paused;
} }
@@ -2425,15 +2429,15 @@ namespace libtorrent
void torrent::set_max_uploads(int limit) void torrent::set_max_uploads(int limit)
{ {
assert(limit >= -1); assert(limit >= -1);
if (limit == -1) limit = std::numeric_limits<int>::max(); if (limit < 0) limit = std::numeric_limits<int>::max();
m_uploads_quota.max = std::max(m_uploads_quota.min, limit); m_max_uploads = limit;
} }
void torrent::set_max_connections(int limit) void torrent::set_max_connections(int limit)
{ {
assert(limit >= -1); assert(limit >= -1);
if (limit == -1) limit = std::numeric_limits<int>::max(); if (limit < -1) limit = std::numeric_limits<int>::max();
m_connections_quota.max = std::max(m_connections_quota.min, limit); m_max_connections = limit;
} }
void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit) void torrent::set_peer_upload_limit(tcp::endpoint ip, int limit)
@@ -2496,6 +2500,14 @@ namespace libtorrent
} }
#endif #endif
#if defined(TORRENT_VERBOSE_LOGGING)
for (peer_iterator i = m_connections.begin();
i != m_connections.end(); ++i)
{
(*i->second->m_logger) << "*** PAUSING TORRENT\n";
}
#endif
disconnect_all(); disconnect_all();
m_paused = true; m_paused = true;
// tell the tracker that we stopped // tell the tracker that we stopped
@@ -2528,10 +2540,6 @@ namespace libtorrent
#endif #endif
m_paused = false; m_paused = false;
m_uploads_quota.min = 0;
m_connections_quota.min = 2;
m_uploads_quota.max = std::numeric_limits<int>::max();
m_connections_quota.max = std::numeric_limits<int>::max();
// tell the tracker that we're back // tell the tracker that we're back
m_event = tracker_request::started; m_event = tracker_request::started;
@@ -2545,10 +2553,6 @@ namespace libtorrent
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
m_connections_quota.used = (int)m_connections.size();
m_uploads_quota.used = m_policy->num_uploads();
m_uploads_quota.max = (int)m_connections.size();
#ifndef TORRENT_DISABLE_EXTENSIONS #ifndef TORRENT_DISABLE_EXTENSIONS
for (extension_list_t::iterator i = m_extensions.begin() for (extension_list_t::iterator i = m_extensions.begin()
, end(m_extensions.end()); i != end; ++i) , end(m_extensions.end()); i != end; ++i)
@@ -2561,10 +2565,6 @@ namespace libtorrent
{ {
// let the stats fade out to 0 // let the stats fade out to 0
m_stat.second_tick(tick_interval); m_stat.second_tick(tick_interval);
m_connections_quota.min = 0;
m_connections_quota.max = 0;
m_uploads_quota.min = 0;
m_uploads_quota.max = 0;
return; return;
} }
@@ -2623,6 +2623,13 @@ namespace libtorrent
} }
accumulator += m_stat; accumulator += m_stat;
m_stat.second_tick(tick_interval); m_stat.second_tick(tick_interval);
m_time_scaler--;
if (m_time_scaler <= 0)
{
m_time_scaler = 10;
m_policy->pulse();
}
} }
bool torrent::try_connect_peer() bool torrent::try_connect_peer()
@@ -2631,18 +2638,6 @@ namespace libtorrent
return m_policy->connect_one_peer(); return m_policy->connect_one_peer();
} }
void torrent::distribute_resources(float tick_interval)
{
INVARIANT_CHECK;
m_time_scaler--;
if (m_time_scaler <= 0)
{
m_time_scaler = settings().unchoke_interval;
m_policy->pulse();
}
}
void torrent::async_verify_piece(int piece_index, boost::function<void(bool)> const& f) void torrent::async_verify_piece(int piece_index, boost::function<void(bool)> const& f)
{ {
INVARIANT_CHECK; INVARIANT_CHECK;
@@ -2764,10 +2759,10 @@ namespace libtorrent
= m_trackers[m_last_working_tracker].url; = m_trackers[m_last_working_tracker].url;
} }
st.num_uploads = m_uploads_quota.used; st.num_uploads = -1;
st.uploads_limit = m_uploads_quota.given; st.uploads_limit = m_max_uploads;
st.num_connections = m_connections_quota.used; st.num_connections = int(m_connections.size());
st.connections_limit = m_connections_quota.given; st.connections_limit = m_max_connections;
// if we don't have any metadata, stop here // if we don't have any metadata, stop here
if (!valid_metadata()) if (!valid_metadata())

View File

@@ -32,7 +32,6 @@ test-suite libtorrent :
[ run test_hasher.cpp ] [ run test_hasher.cpp ]
[ run test_metadata_extension.cpp ] [ run test_metadata_extension.cpp ]
[ run test_swarm.cpp ] [ run test_swarm.cpp ]
[ run test_allocate_resources.cpp ]
[ run test_web_seed.cpp ] [ run test_web_seed.cpp ]
[ run test_bandwidth_limiter.cpp ] [ run test_bandwidth_limiter.cpp ]
; ;

View File

@@ -1,57 +0,0 @@
#include "libtorrent/aux_/allocate_resources_impl.hpp"
#include <boost/utility.hpp>
#include "test.hpp"
using namespace libtorrent;
struct resource_entry
{
resource_entry(resource_request r_): r(r_) {}
resource_request r;
};
void fill_client_vector(std::vector<resource_entry>& v)
{
v.push_back(resource_request(5000, 20, 20000, 10000));
v.push_back(resource_request(9000, 20, 20000, 10000));
v.push_back(resource_request(8000, 20, 20000, 10000));
v.push_back(resource_request(7000, 20, 20000, 10000));
v.push_back(resource_request(5000, 20, 20000, 10000));
v.push_back(resource_request(8000, 20, 20000, 10000));
}
void check_client_vec(std::vector<resource_entry> const& v, int resources)
{
int sum = 0;
int min_sum = 0;
for (std::vector<resource_entry>::const_iterator i = v.begin()
, end(v.end()); i != end; ++i)
{
TEST_CHECK(i->r.given >= i->r.min);
TEST_CHECK(i->r.given <= i->r.max);
sum += i->r.given;
min_sum += i->r.min;
}
TEST_CHECK(sum <= (std::max)(resources, min_sum));
}
int test_main()
{
using namespace libtorrent;
std::vector<resource_entry> clients;
fill_client_vector(clients);
using aux::allocate_resources_impl;
allocate_resources_impl(20, clients.begin(), clients.end(), &resource_entry::r);
check_client_vec(clients, 20);
fill_client_vector(clients);
allocate_resources_impl(20000, clients.begin(), clients.end(), &resource_entry::r);
check_client_vec(clients, 20000);
return 0;
}