forked from premiere/premiere-libtorrent
Merge pull request #167 from arvidn/auto-manage-fix
simplify the queuing logic for checking torrents
This commit is contained in:
commit
a16c24dc0b
|
@ -1,3 +1,5 @@
|
|||
* make all non-auto managed torrents exempt from queuing logic, including
|
||||
checking torrents.
|
||||
* add option to not proxy tracker connections through proxy
|
||||
* removed sparse-regions feature
|
||||
* support using 0 disk threads (to perform disk I/O in network thread)
|
||||
|
|
195
docs/manual.rst
195
docs/manual.rst
|
@ -174,20 +174,130 @@ The format of the magnet URI is:
|
|||
queuing
|
||||
=======
|
||||
|
||||
libtorrent supports *queuing*. Which means it makes sure that a limited number of
|
||||
torrents are being downloaded at any given time, and once a torrent is completely
|
||||
downloaded, the next in line is started.
|
||||
libtorrent supports *queuing*. Queuing is a mechanism to automatically pause and
|
||||
resume torrents based on certain criteria. The criteria depends on the overall
|
||||
state the torrent is in (checking, downloading or seeding).
|
||||
|
||||
Torrents that are *auto managed* are subject to the queuing and the active
|
||||
torrents limits. To make a torrent auto managed, set add_torrent_params::flag_auto_managed
|
||||
when adding the torrent (see async_add_torrent() and add_torrent()).
|
||||
To opt-out of the queuing logic, make sure your torrents are added with the
|
||||
add_torrent_params::flag_auto_managed bit *cleared*. Or call
|
||||
``torrent_handle::auto_managed(false)`` on the torrent handle.
|
||||
|
||||
The limits of the number of downloading and seeding torrents are controlled via
|
||||
settings_pack::active_downloads, settings_pack::active_seeds and settings_pack::active_limit in
|
||||
settings_pack. These limits takes non auto managed torrents into account as
|
||||
well. If there are more non-auto managed torrents being downloaded than the
|
||||
settings_pack::active_downloads setting, any auto managed torrents will be queued until
|
||||
torrents are removed so that the number drops below the limit.
|
||||
The overall purpose of the queuing logic is to improve performance under arbitrary
|
||||
torrent downloading and seeding load. For example, if you want to download 100
|
||||
torrents on a limited home connection, you improve performance by downloading
|
||||
them one at a time (or maybe two at a time), over downloading them all in
|
||||
parallel. The benefits are:
|
||||
|
||||
* the average completion time of a torrent is half of what it would be if all
|
||||
downloaded in parallel.
|
||||
* The amount of upload capacity is more likely to reach the *reciprocation rate*
|
||||
of your peers, and is likely to improve your *return on investment* (download
|
||||
to upload ratio)
|
||||
* your disk I/O load is likely to be more local which may improve I/O
|
||||
performance and decrease fragmentation.
|
||||
|
||||
There are fundamentally 3 seaparate queues:
|
||||
|
||||
* checking torrents
|
||||
* downloading torrents
|
||||
* seeding torrents
|
||||
|
||||
Every torrent that is not seeding has a queue number associated with it, this is
|
||||
its place in line to be started. See torrent_status::queue_position.
|
||||
|
||||
On top of the limits of each queue, there is an over arching limit, set in
|
||||
settings_pack::active_limit. The auto manager will never start more than this
|
||||
number of torrents (with one exception described below). Non-auto-managed
|
||||
torrents are exempt from this logic, and not counted.
|
||||
|
||||
At a regular interval, torrents are checked if there needs to be any
|
||||
re-ordering of which torrents are active and which are queued. This interval
|
||||
can be controlled via settings_pack::auto_manage_interval.
|
||||
|
||||
For queuing to work, resume data needs to be saved and restored for all
|
||||
torrents. See torrent_handle::save_resume_data().
|
||||
|
||||
queue position
|
||||
--------------
|
||||
|
||||
The torrents in the front of the queue are started and the rest are ordered by
|
||||
their queue position. Any newly added torrent is placed at the end of the queue.
|
||||
Once a torrent is removed or turns into a seed, its queue position is -1 and all
|
||||
torrents that used to be after it in the queue, decreases their position in
|
||||
order to fill the gap.
|
||||
|
||||
The queue positions are always contiguous, in a sequence without any gaps.
|
||||
|
||||
Lower queue position means closer to the front of the queue, and will be
|
||||
started sooner than torrents with higher queue positions.
|
||||
|
||||
To query a torrent for its position in the queue, or change its position, see:
|
||||
torrent_handle::queue_position(), torrent_handle::queue_position_up(),
|
||||
torrent_handle::queue_position_down(), torrent_handle::queue_position_top()
|
||||
and torrent_handle::queue_position_bottom().
|
||||
|
||||
checking queue
|
||||
--------------
|
||||
|
||||
The checking queue affects torrents in the torrent_status::checking or
|
||||
torrent_status::allocating state that are auto-managed.
|
||||
|
||||
The checking queue will make sure that (of the torrents in its queue) no more than
|
||||
settings_pack::active_checking_limit torrents are started at any given time.
|
||||
Once a torrent completes checking and moves into a diffferent state, the next in
|
||||
line will be started for checking.
|
||||
|
||||
Any torrent added force-started or force-stopped (i.e. the auto managed flag is
|
||||
_not_ set), will not be subject to this limit and they will all check
|
||||
independently and in parallel.
|
||||
|
||||
downloading queue
|
||||
-----------------
|
||||
|
||||
Similarly to the checking queue, the downloading queue will make sure that no
|
||||
more than settings_pack::active_downloads torrents are in the downloading
|
||||
state at any given time.
|
||||
|
||||
The torrent_status::queue_position is used again here to determine who is next
|
||||
in line to be started once a downloading torrent completes or is stopped/removed.
|
||||
|
||||
seeding queue
|
||||
-------------
|
||||
|
||||
The seeding queue does not use torrent_status::queue_position to determine which
|
||||
torrent to seed. Instead, it estimates the *demand* for the torrent to be
|
||||
seeded. A torrent with few other seeds and many downloaders is assumed to have a
|
||||
higher demand of more seeds than one with many seeds and few downloaders.
|
||||
|
||||
It limits the number of started seeds to settings_pack::active_seeds.
|
||||
|
||||
On top of this basic bias, *seed priority* can be controller by specifying a
|
||||
seed ratio (the upload to download ratio), a seed-time ratio (the download
|
||||
time to seeding time ratio) and a seed-time (the abosulte time to be seeding a
|
||||
torrent). Until all those targets are hit, the torrent will be prioritized for
|
||||
seeding.
|
||||
|
||||
Among torrents that have met their seed target, torrents where we don't know of
|
||||
any other seed take strict priority.
|
||||
|
||||
In order to avoid flapping, torrents that were started less than 30 minutes ago
|
||||
also have priority to keep seeding.
|
||||
|
||||
Finally, for torrents where none of the above apply, they are prioritized based
|
||||
on the download to seed ratio.
|
||||
|
||||
The relevant settings to control these limits are
|
||||
settings_pack::share_ratio_limit, settings_pack::seed_time_ratio_limit and
|
||||
settings_pack::seed_time_limit.
|
||||
|
||||
queuing options
|
||||
---------------
|
||||
|
||||
In addition to simply starting and stopping torrents, the queuing mechanism can
|
||||
have more fine grained control of the resources used by torrents.
|
||||
|
||||
half-started torrents
|
||||
.....................
|
||||
|
||||
In addition to the downloading and seeding limits, there are limits on *actions*
|
||||
torrents perform. The downloading and seeding limits control whether peers are
|
||||
|
@ -203,49 +313,36 @@ limit. These limits are controlled by settings_pack::active_tracker_limit,
|
|||
settings_pack::active_dht_limit and settings_pack::active_lsd_limit
|
||||
respectively.
|
||||
|
||||
A client that is not concerned about the separate costs of these actions should
|
||||
set all 3 of these limits to the same value as settings_pack::active_limit (i.e.
|
||||
the max limit of any active torrent).
|
||||
Specifically, announcing to a tracker is typically cheaper than
|
||||
announcing to the DHT. ``active_dht_limit`` will limit the number of
|
||||
torrents that are allowed to announce to the DHT. The highest priority ones
|
||||
will, and the lower priority ones won't. The will still be considered started
|
||||
though, and any incoming peers will still be accepted.
|
||||
|
||||
At a regular interval, torrents are checked if there needs to be any
|
||||
re-ordering of which torrents are active and which are queued. This interval
|
||||
can be controlled via settings_pack::auto_manage_interval.
|
||||
If you do not wish to impose such limits (basically, if you do not wish to have
|
||||
half-started torrents) make sure to set these limits to -1 (infinite).
|
||||
|
||||
For queuing to work, resume data needs to be saved and restored for all
|
||||
torrents. See save_resume_data().
|
||||
prefer seeds
|
||||
............
|
||||
|
||||
downloading
|
||||
-----------
|
||||
In the case where ``active_downloads`` + ``active_seeds`` > ``active_limit``,
|
||||
there's an ambiguity whether the downloads should be satisfied first or the
|
||||
seeds. To disambiguate this case, the settings_pack::auto_manage_prefer_seeds
|
||||
determines whether seeds are preferred or not.
|
||||
|
||||
Torrents that are currently being downloaded or incomplete (with bytes still to
|
||||
download) are queued. The torrents in the front of the queue are started to be
|
||||
actively downloaded and the rest are ordered with regards to their queue
|
||||
position. Any newly added torrent is placed at the end of the queue. Once a
|
||||
torrent is removed or turns into a seed, its queue position is -1 and all
|
||||
torrents that used to be after it in the queue, decreases their position in
|
||||
order to fill the gap.
|
||||
inactive torrents
|
||||
.................
|
||||
|
||||
The queue positions are always in a sequence without any gaps.
|
||||
Torrents that are not transferring any bytes (downloading or uploading) have a
|
||||
relatively low cost to be started. It's possible to exempt such torrents from
|
||||
the download and seed queues by setting settings_pack::dont_count_slow_torrents
|
||||
to true.
|
||||
|
||||
Lower queue position means closer to the front of the queue, and will be
|
||||
started sooner than torrents with higher queue positions.
|
||||
|
||||
To query a torrent for its position in the queue, or change its position, see:
|
||||
queue_position(), queue_position_up(), queue_position_down(),
|
||||
queue_position_top() and queue_position_bottom().
|
||||
|
||||
seeding
|
||||
-------
|
||||
|
||||
Auto managed seeding torrents are rotated, so that all of them are allocated a
|
||||
fair amount of seeding. Torrents with fewer completed *seed cycles* are
|
||||
prioritized for seeding. A seed cycle is completed when a torrent meets either
|
||||
the share ratio limit (uploaded bytes / downloaded bytes), the share time ratio
|
||||
(time seeding / time downloaing) or seed time limit (time seeded).
|
||||
|
||||
The relevant settings to control these limits are
|
||||
settings_pack::share_ratio_limit, settings_pack::seed_time_ratio_limit and
|
||||
settings_pack::seed_time_limit.
|
||||
Since it sometimes may take a few minutes for a newly started torrent to find
|
||||
peers and be unchoked, or find peers that are interested in requesting data,
|
||||
torrents are not considered inactive immadiately. There must be an extended
|
||||
period of no transfers before it is considered inactive and exempt from the
|
||||
queuing limits.
|
||||
|
||||
fast resume
|
||||
===========
|
||||
|
|
|
@ -978,6 +978,10 @@ namespace libtorrent
|
|||
// ``active_downloads`` and ``active_seeds`` are upper limits on the
|
||||
// number of downloading torrents and seeding torrents respectively.
|
||||
// Setting the value to -1 means unlimited.
|
||||
//
|
||||
// ``active_checking`` is the limit of number of checking torrents.
|
||||
// Note that this limit applies to started non-auto-managed torrents as
|
||||
// well (as long as they are the the checking_files state).
|
||||
//
|
||||
// For example if there are 10 seeding torrents and 10 downloading
|
||||
// torrents, and ``active_downloads`` is 4 and ``active_seeds`` is 4,
|
||||
|
@ -987,8 +991,9 @@ namespace libtorrent
|
|||
// active. Torrents that are not auto managed are not counted against
|
||||
// these limits.
|
||||
//
|
||||
// ``active_limit`` is a hard limit on the number of active torrents.
|
||||
// This applies even to slow torrents.
|
||||
// ``active_limit`` is a hard limit on the number of active (auto
|
||||
// managed) torrents. This limit also applies to slow torrents. It does
|
||||
// not apply to checking torrents.
|
||||
//
|
||||
// ``active_dht_limit`` is the max number of torrents to announce to
|
||||
// the DHT. By default this is set to 88, which is no more than one
|
||||
|
@ -1019,6 +1024,7 @@ namespace libtorrent
|
|||
// see dynamic-loading-of-torrent-files_.
|
||||
active_downloads,
|
||||
active_seeds,
|
||||
active_checking,
|
||||
active_dht_limit,
|
||||
active_tracker_limit,
|
||||
active_lsd_limit,
|
||||
|
|
|
@ -1458,9 +1458,11 @@ namespace libtorrent
|
|||
|
||||
// ----
|
||||
|
||||
// total time we've been available on this torrent
|
||||
// does not count when the torrent is stopped or paused
|
||||
// in seconds
|
||||
// total time we've been active on this torrent. i.e. either (trying to)
|
||||
// download or seed. does not count time when the torrent is stopped or
|
||||
// paused. specified in seconds. This only track time _before_ we started
|
||||
// the torrent this last time. When the torrent is paused, this counter is
|
||||
// incremented to include this current session.
|
||||
unsigned int m_active_time:24;
|
||||
|
||||
// the index to the last tracker that worked
|
||||
|
@ -1468,8 +1470,8 @@ namespace libtorrent
|
|||
|
||||
// ----
|
||||
|
||||
// total time we've been finished with this torrent
|
||||
// does not count when the torrent is stopped or paused
|
||||
// total time we've been finished with this torrent.
|
||||
// does not count when the torrent is stopped or paused.
|
||||
unsigned int m_finished_time:24;
|
||||
|
||||
// in case the piece picker hasn't been constructed
|
||||
|
@ -1512,8 +1514,11 @@ namespace libtorrent
|
|||
|
||||
// ----
|
||||
|
||||
// total time we've been available as a seed on this torrent
|
||||
// does not count when the torrent is stopped or paused
|
||||
// total time we've been available as a seed on this torrent.
|
||||
// does not count when the torrent is stopped or paused. This value only
|
||||
// accounts for the time prior to the current start of the torrent. When
|
||||
// the torrent is paused, this counter is incremented to account for the
|
||||
// additional seeding time.
|
||||
unsigned int m_seeding_time:24;
|
||||
|
||||
// ----
|
||||
|
|
|
@ -545,10 +545,12 @@ namespace libtorrent
|
|||
// requested from it, it is disconnected. This is a graceful shut down of
|
||||
// the torrent in the sense that no downloaded bytes are wasted.
|
||||
//
|
||||
// torrents that are auto-managed may be automatically resumed again. It
|
||||
// does not make sense to pause an auto-managed torrent without making it
|
||||
// not automanaged first. Torrents are auto-managed by default when added
|
||||
// to the session. For more information, see queuing_.
|
||||
// .. note::
|
||||
// Torrents that are auto-managed may be automatically resumed again. It
|
||||
// does not make sense to pause an auto-managed torrent without making it
|
||||
// not automanaged first. Torrents are auto-managed by default when added
|
||||
// to the session. For more information, see queuing_.
|
||||
//
|
||||
void pause(int flags = 0) const;
|
||||
void resume() const;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ project
|
|||
;
|
||||
|
||||
alias libtorrent-sims :
|
||||
[ run test_auto_manage.cpp ]
|
||||
[ run test_torrent_status.cpp ]
|
||||
[ run test_swarm.cpp ]
|
||||
[ run test_super_seeding.cpp ]
|
||||
|
|
|
@ -82,7 +82,8 @@ struct swarm
|
|||
m_torrents.push_back(lt::torrent_handle());
|
||||
|
||||
lt::add_torrent_params params = m_config.add_torrent(i);
|
||||
ses->async_add_torrent(params);
|
||||
if (!params.save_path.empty())
|
||||
ses->async_add_torrent(params);
|
||||
|
||||
ses->set_alert_notify(boost::bind(&swarm::on_alert_notify, this, i));
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ struct swarm_config : swarm_setup_provider
|
|||
if (ec) fprintf(stderr, "failed to create directory: \"%s\": %s\n"
|
||||
, path.c_str(), ec.message().c_str());
|
||||
std::ofstream file(combine_path(path, "temporary").c_str());
|
||||
m_ti = ::create_torrent(&file, 0x4000, 9, false);
|
||||
m_ti = ::create_torrent(&file, "temporary", 0x4000, 9, false);
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,626 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2015, Arvid Norberg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "libtorrent/session.hpp"
|
||||
#include "libtorrent/torrent_handle.hpp"
|
||||
#include "libtorrent/settings_pack.hpp"
|
||||
#include "libtorrent/alert_types.hpp"
|
||||
#include "libtorrent/deadline_timer.hpp"
|
||||
#include "test.hpp"
|
||||
#include "swarm_config.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "simulator/simulator.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace sim;
|
||||
|
||||
const int num_torrents = 10;
|
||||
|
||||
lt::add_torrent_params create_torrent(int idx, bool seed)
|
||||
{
|
||||
// TODO: if we want non-seeding torrents, that could be a bit cheaper to
|
||||
// create
|
||||
lt::add_torrent_params params;
|
||||
int swarm_id = test_counter();
|
||||
char name[200];
|
||||
snprintf(name, sizeof(name), "temp-%02d", idx);
|
||||
char path[200];
|
||||
snprintf(path, sizeof(path), "swarm-%04d-peer-%02d"
|
||||
, swarm_id, idx);
|
||||
error_code ec;
|
||||
create_directory(path, ec);
|
||||
if (ec) fprintf(stderr, "failed to create directory: \"%s\": %s\n"
|
||||
, path, ec.message().c_str());
|
||||
std::ofstream file(combine_path(path, name).c_str());
|
||||
params.ti = ::create_torrent(&file, name
|
||||
, 0x4000, 9 + idx, false);
|
||||
file.close();
|
||||
|
||||
// by setting the save path to a dummy path, it won't be seeding
|
||||
params.save_path = seed ? path : "dummy";
|
||||
return params;
|
||||
}
|
||||
|
||||
using sim::asio::ip::address_v4;
|
||||
|
||||
std::unique_ptr<sim::asio::io_service> make_io_service(sim::simulation& sim, int i)
|
||||
{
|
||||
char ep[30];
|
||||
snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff);
|
||||
return std::unique_ptr<sim::asio::io_service>(new sim::asio::io_service(
|
||||
sim, asio::ip::address_v4::from_string(ep)));
|
||||
}
|
||||
|
||||
// this is the general template for these tests. create the session with custom
|
||||
// settings (Settings), set up the test, by adding torrents with certain
|
||||
// arguments (Setup), run the test and verify the end state (Test)
|
||||
template <typename Settings, typename Setup, typename Test>
|
||||
void run_test(Settings const& sett, Setup const& setup, Test const& test)
|
||||
{
|
||||
// setup the simulation
|
||||
sim::default_config network_cfg;
|
||||
sim::simulation sim{network_cfg};
|
||||
std::unique_ptr<sim::asio::io_service> ios = make_io_service(sim, 0);
|
||||
lt::session_proxy zombie;
|
||||
|
||||
// setup settings pack to use for the session (customization point)
|
||||
lt::settings_pack pack = settings();
|
||||
sett(pack);
|
||||
|
||||
// create session
|
||||
std::shared_ptr<lt::session> ses = std::make_shared<lt::session>(pack, *ios);
|
||||
|
||||
// set up test, like adding torrents (customization point)
|
||||
setup(*ses);
|
||||
|
||||
// set up a timer to fire later, to verify everything we expected to happen
|
||||
// happened
|
||||
lt::deadline_timer timer(*ios);
|
||||
timer.expires_from_now(lt::seconds((num_torrents + 1) * 60));
|
||||
timer.async_wait([&](boost::system::error_code const& ec)
|
||||
{
|
||||
test(*ses);
|
||||
|
||||
// shut down
|
||||
zombie = ses->abort();
|
||||
ses.reset();
|
||||
});
|
||||
|
||||
sim.run();
|
||||
}
|
||||
|
||||
TORRENT_TEST(dont_count_slow_torrents)
|
||||
{
|
||||
run_test(
|
||||
[](settings_pack& sett) {
|
||||
// session settings
|
||||
sett.set_bool(settings_pack::dont_count_slow_torrents, true);
|
||||
sett.set_int(settings_pack::active_downloads, 1);
|
||||
sett.set_int(settings_pack::active_seeds, 1);
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// add torrents
|
||||
for (int i = 0; i < num_torrents; ++i)
|
||||
{
|
||||
lt::add_torrent_params params = create_torrent(i, false);
|
||||
params.flags |= add_torrent_params::flag_auto_managed;
|
||||
params.flags |= add_torrent_params::flag_paused;
|
||||
ses.async_add_torrent(params);
|
||||
}
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// verify result
|
||||
|
||||
std::vector<lt::alert*> alerts;
|
||||
ses.pop_alerts(&alerts);
|
||||
|
||||
lt::time_point last = lt::time_point::min();
|
||||
lt::time_point start_time = alerts[0]->timestamp();
|
||||
|
||||
int num_started = 0;
|
||||
for (alert* a : alerts)
|
||||
{
|
||||
printf("%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
|
||||
- start_time).count()), a->message().c_str());
|
||||
if (alert_cast<torrent_resumed_alert>(a) == nullptr) continue;
|
||||
|
||||
lt::time_point t = a->timestamp();
|
||||
if (last != lt::time_point::min())
|
||||
{
|
||||
// expect starting of new torrents to be spaced by 60 seconds
|
||||
// the division by 2 is to allow some slack (it's integer
|
||||
// division)
|
||||
TEST_EQUAL(duration_cast<lt::seconds>(t - last).count() / 2, 60 / 2);
|
||||
}
|
||||
last = t;
|
||||
++num_started;
|
||||
}
|
||||
|
||||
TEST_EQUAL(num_started, num_torrents);
|
||||
|
||||
for (torrent_handle const& h : ses.get_torrents())
|
||||
{
|
||||
TEST_CHECK(h.status().auto_managed);
|
||||
TEST_EQUAL(h.status().paused, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TORRENT_TEST(count_slow_torrents)
|
||||
{
|
||||
run_test(
|
||||
[](settings_pack& sett) {
|
||||
// session settings
|
||||
sett.set_bool(settings_pack::dont_count_slow_torrents, false);
|
||||
sett.set_int(settings_pack::active_downloads, 1);
|
||||
sett.set_int(settings_pack::active_seeds, 1);
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// add torrents
|
||||
for (int i = 0; i < num_torrents; ++i)
|
||||
{
|
||||
lt::add_torrent_params params = create_torrent(i, false);
|
||||
params.flags |= add_torrent_params::flag_auto_managed;
|
||||
params.flags |= add_torrent_params::flag_paused;
|
||||
ses.async_add_torrent(params);
|
||||
}
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// verify result (only one should have been started, even though
|
||||
// they're all idle)
|
||||
|
||||
std::vector<lt::alert*> alerts;
|
||||
ses.pop_alerts(&alerts);
|
||||
|
||||
lt::time_point start_time = alerts[0]->timestamp();
|
||||
|
||||
int num_started = 0;
|
||||
for (alert* a : alerts)
|
||||
{
|
||||
printf("%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
|
||||
- start_time).count()), a->message().c_str());
|
||||
if (alert_cast<torrent_resumed_alert>(a) == nullptr) continue;
|
||||
++num_started;
|
||||
}
|
||||
|
||||
TEST_EQUAL(num_started, 1);
|
||||
|
||||
num_started = 0;
|
||||
for (torrent_handle const& h : ses.get_torrents())
|
||||
{
|
||||
TEST_CHECK(h.status().auto_managed);
|
||||
num_started += !h.status().paused;
|
||||
}
|
||||
TEST_EQUAL(num_started, 1);
|
||||
});
|
||||
}
|
||||
|
||||
TORRENT_TEST(force_stopped_download)
|
||||
{
|
||||
run_test(
|
||||
[](settings_pack& sett) {
|
||||
// session settings
|
||||
sett.set_bool(settings_pack::dont_count_slow_torrents, true);
|
||||
sett.set_int(settings_pack::active_downloads, 10);
|
||||
sett.set_int(settings_pack::active_seeds, 10);
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// add torrents
|
||||
for (int i = 0; i < num_torrents; ++i)
|
||||
{
|
||||
lt::add_torrent_params params = create_torrent(i, false);
|
||||
// torrents are paused and not auto-managed
|
||||
params.flags &= ~add_torrent_params::flag_auto_managed;
|
||||
params.flags |= add_torrent_params::flag_paused;
|
||||
ses.async_add_torrent(params);
|
||||
}
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// verify result (none should have been started)
|
||||
|
||||
std::vector<lt::alert*> alerts;
|
||||
ses.pop_alerts(&alerts);
|
||||
|
||||
lt::time_point start_time = alerts[0]->timestamp();
|
||||
|
||||
for (alert* a : alerts)
|
||||
{
|
||||
printf("%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
|
||||
- start_time).count()), a->message().c_str());
|
||||
// we don't expect any torrents being started or stopped, since
|
||||
// they're all force stopped
|
||||
TEST_CHECK(alert_cast<torrent_resumed_alert>(a) == nullptr);
|
||||
TEST_CHECK(alert_cast<torrent_paused_alert>(a) == nullptr);
|
||||
}
|
||||
|
||||
for (torrent_handle const& h : ses.get_torrents())
|
||||
{
|
||||
TEST_CHECK(!h.status().auto_managed);
|
||||
TEST_CHECK(h.status().paused);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TORRENT_TEST(force_started)
|
||||
{
|
||||
run_test(
|
||||
[](settings_pack& sett) {
|
||||
// session settings
|
||||
sett.set_bool(settings_pack::dont_count_slow_torrents, false);
|
||||
sett.set_int(settings_pack::active_downloads, 1);
|
||||
sett.set_int(settings_pack::active_seeds, 1);
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// add torrents
|
||||
for (int i = 0; i < num_torrents; ++i)
|
||||
{
|
||||
lt::add_torrent_params params = create_torrent(i, false);
|
||||
// torrents are started and not auto-managed
|
||||
params.flags &= ~add_torrent_params::flag_auto_managed;
|
||||
params.flags &= ~add_torrent_params::flag_paused;
|
||||
ses.async_add_torrent(params);
|
||||
}
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// verify result (none should have been started)
|
||||
|
||||
std::vector<lt::alert*> alerts;
|
||||
ses.pop_alerts(&alerts);
|
||||
|
||||
lt::time_point start_time = alerts[0]->timestamp();
|
||||
|
||||
for (alert* a : alerts)
|
||||
{
|
||||
printf("%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
|
||||
- start_time).count()), a->message().c_str());
|
||||
// we don't expect any torrents being started or stopped, since
|
||||
// they're all force started
|
||||
TEST_CHECK(alert_cast<torrent_resumed_alert>(a) == nullptr);
|
||||
TEST_CHECK(alert_cast<torrent_paused_alert>(a) == nullptr);
|
||||
}
|
||||
|
||||
for (torrent_handle const& h : ses.get_torrents())
|
||||
{
|
||||
TEST_CHECK(!h.status().auto_managed);
|
||||
TEST_CHECK(!h.status().paused);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TORRENT_TEST(seed_limit)
|
||||
{
|
||||
run_test(
|
||||
[](settings_pack& sett) {
|
||||
// session settings
|
||||
// set the seed limit to 3
|
||||
sett.set_bool(settings_pack::dont_count_slow_torrents, false);
|
||||
sett.set_int(settings_pack::active_checking, 1);
|
||||
sett.set_int(settings_pack::active_seeds, 3);
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// add torrents
|
||||
// add 5 seeds
|
||||
for (int i = 0; i < num_torrents; ++i)
|
||||
{
|
||||
lt::add_torrent_params params = create_torrent(i, true);
|
||||
// torrents are paused and auto-managed
|
||||
params.flags |= add_torrent_params::flag_auto_managed;
|
||||
params.flags |= add_torrent_params::flag_paused;
|
||||
ses.async_add_torrent(params);
|
||||
}
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// verify result (none should have been started)
|
||||
// make sure only 3 got started
|
||||
std::vector<lt::alert*> alerts;
|
||||
ses.pop_alerts(&alerts);
|
||||
|
||||
lt::time_point start_time = alerts[0]->timestamp();
|
||||
|
||||
int num_started = 0;
|
||||
int num_checking = 0;
|
||||
int num_seeding = 0;
|
||||
for (alert* a : alerts)
|
||||
{
|
||||
fprintf(stderr, "%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
|
||||
- start_time).count()), a->message().c_str());
|
||||
if (alert_cast<torrent_resumed_alert>(a))
|
||||
{
|
||||
++num_started;
|
||||
|
||||
fprintf(stderr, "started: %d checking: %d seeding: %d\n"
|
||||
, num_started, num_checking, num_seeding);
|
||||
}
|
||||
else if (alert_cast<torrent_paused_alert>(a))
|
||||
{
|
||||
TEST_CHECK(num_started > 0);
|
||||
--num_started;
|
||||
|
||||
fprintf(stderr, "started: %d checking: %d seeding: %d\n"
|
||||
, num_started, num_checking, num_seeding);
|
||||
}
|
||||
else if (state_changed_alert* sc = alert_cast<state_changed_alert>(a))
|
||||
{
|
||||
if (sc->prev_state == torrent_status::checking_files)
|
||||
--num_checking;
|
||||
else if (sc->prev_state == torrent_status::seeding)
|
||||
--num_seeding;
|
||||
|
||||
if (sc->state == torrent_status::checking_files)
|
||||
++num_checking;
|
||||
else if (sc->state == torrent_status::seeding)
|
||||
++num_seeding;
|
||||
|
||||
fprintf(stderr, "started: %d checking: %d seeding: %d\n"
|
||||
, num_started, num_checking, num_seeding);
|
||||
|
||||
// while at least one torrent is checking, there may be another
|
||||
// started torrent (the checking one), other than that, only 3
|
||||
// torrents are allowed to be started and seeding
|
||||
TEST_CHECK(num_started <= 3 + 1);
|
||||
TEST_CHECK(num_started <= 1 || num_seeding > 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_EQUAL(num_started, 3);
|
||||
|
||||
num_started = 0;
|
||||
for (torrent_handle const& h : ses.get_torrents())
|
||||
{
|
||||
TEST_CHECK(h.status().auto_managed);
|
||||
TEST_CHECK(h.status().is_seeding);
|
||||
num_started += !h.status().paused;
|
||||
}
|
||||
TEST_EQUAL(num_started, 3);
|
||||
});
|
||||
}
|
||||
|
||||
TORRENT_TEST(download_limit)
|
||||
{
|
||||
run_test(
|
||||
[](settings_pack& sett) {
|
||||
// session settings
|
||||
// set the seed limit to 3
|
||||
sett.set_bool(settings_pack::dont_count_slow_torrents, false);
|
||||
sett.set_int(settings_pack::active_checking, 1);
|
||||
sett.set_int(settings_pack::active_downloads, 3);
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// add torrents
|
||||
// add 5 seeds
|
||||
for (int i = 0; i < num_torrents; ++i)
|
||||
{
|
||||
lt::add_torrent_params params = create_torrent(i, false);
|
||||
// torrents are paused and auto-managed
|
||||
params.flags |= add_torrent_params::flag_auto_managed;
|
||||
params.flags |= add_torrent_params::flag_paused;
|
||||
ses.async_add_torrent(params);
|
||||
}
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// verify result (none should have been started)
|
||||
// make sure only 3 got started
|
||||
std::vector<lt::alert*> alerts;
|
||||
ses.pop_alerts(&alerts);
|
||||
|
||||
lt::time_point start_time = alerts[0]->timestamp();
|
||||
|
||||
int num_started = 0;
|
||||
int num_checking = 0;
|
||||
int num_downloading = 0;
|
||||
for (alert* a : alerts)
|
||||
{
|
||||
fprintf(stderr, "%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
|
||||
- start_time).count()), a->message().c_str());
|
||||
if (alert_cast<torrent_resumed_alert>(a))
|
||||
{
|
||||
++num_started;
|
||||
|
||||
fprintf(stderr, "started: %d checking: %d downloading: %d\n"
|
||||
, num_started, num_checking, num_downloading);
|
||||
}
|
||||
else if (alert_cast<torrent_paused_alert>(a))
|
||||
{
|
||||
TEST_CHECK(num_started > 0);
|
||||
--num_started;
|
||||
|
||||
fprintf(stderr, "started: %d checking: %d downloading: %d\n"
|
||||
, num_started, num_checking, num_downloading);
|
||||
}
|
||||
else if (state_changed_alert* sc = alert_cast<state_changed_alert>(a))
|
||||
{
|
||||
if (sc->prev_state == torrent_status::checking_files)
|
||||
--num_checking;
|
||||
else if (sc->prev_state == torrent_status::downloading)
|
||||
--num_downloading;
|
||||
|
||||
if (sc->state == torrent_status::checking_files)
|
||||
++num_checking;
|
||||
else if (sc->state == torrent_status::downloading)
|
||||
++num_downloading;
|
||||
|
||||
fprintf(stderr, "started: %d checking: %d downloading: %d\n"
|
||||
, num_started, num_checking, num_downloading);
|
||||
|
||||
// while at least one torrent is checking, there may be another
|
||||
// started torrent (the checking one), other than that, only 3
|
||||
// torrents are allowed to be started and seeding
|
||||
TEST_CHECK(num_started <= 3 + 1);
|
||||
TEST_CHECK(num_started <= 1 || num_downloading > 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_EQUAL(num_started, 3);
|
||||
|
||||
num_started = 0;
|
||||
for (torrent_handle const& h : ses.get_torrents())
|
||||
{
|
||||
TEST_CHECK(h.status().auto_managed);
|
||||
TEST_CHECK(!h.status().is_finished);
|
||||
num_started += !h.status().paused;
|
||||
}
|
||||
TEST_EQUAL(num_started, 3);
|
||||
});
|
||||
}
|
||||
// make sure torrents don't announce to the tracker when transitioning from
|
||||
// checking to paused downloading
|
||||
TORRENT_TEST(checking_announce)
|
||||
{
|
||||
run_test(
|
||||
[](settings_pack& sett) {
|
||||
// session settings
|
||||
// set the seed limit to 3
|
||||
sett.set_bool(settings_pack::dont_count_slow_torrents, false);
|
||||
sett.set_int(settings_pack::active_checking, 1);
|
||||
|
||||
// just set the tracker retry intervals really long, to make sure we
|
||||
// don't keep retrying the tracker (since there's nothing running
|
||||
// there, it will fail)
|
||||
sett.set_int(settings_pack::tracker_backoff, 100000);
|
||||
// only the first torrent added should ever announce
|
||||
sett.set_int(settings_pack::active_seeds, 1);
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// add torrents
|
||||
// add 5 seeds
|
||||
for (int i = 0; i < num_torrents; ++i)
|
||||
{
|
||||
lt::add_torrent_params params = create_torrent(i, true);
|
||||
// torrents are paused and auto-managed
|
||||
params.flags |= add_torrent_params::flag_auto_managed;
|
||||
params.flags |= add_torrent_params::flag_paused;
|
||||
// we need this to get the tracker_announce_alert
|
||||
params.trackers.push_back("http://10.10.0.2/announce");
|
||||
ses.async_add_torrent(params);
|
||||
}
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// verify result (none should have been started)
|
||||
// make sure only 3 got started
|
||||
std::vector<lt::alert*> alerts;
|
||||
ses.pop_alerts(&alerts);
|
||||
|
||||
lt::time_point start_time = alerts[0]->timestamp();
|
||||
|
||||
int num_announce = 0;
|
||||
for (alert* a : alerts)
|
||||
{
|
||||
fprintf(stderr, "%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
|
||||
- start_time).count()), a->message().c_str());
|
||||
if (alert_cast<tracker_announce_alert>(a))
|
||||
++num_announce;
|
||||
}
|
||||
|
||||
TEST_EQUAL(num_announce, 1);
|
||||
|
||||
int num_started = 0;
|
||||
for (torrent_handle const& h : ses.get_torrents())
|
||||
{
|
||||
TEST_CHECK(h.status().auto_managed);
|
||||
num_started += !h.status().paused;
|
||||
}
|
||||
TEST_EQUAL(num_started, 1);
|
||||
});
|
||||
}
|
||||
|
||||
TORRENT_TEST(paused_checking)
|
||||
{
|
||||
run_test(
|
||||
[](settings_pack& sett) {
|
||||
// session settings
|
||||
// set the seed limit to 3
|
||||
sett.set_bool(settings_pack::dont_count_slow_torrents, true);
|
||||
sett.set_int(settings_pack::active_checking, 1);
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// add torrents
|
||||
// add 5 seeds
|
||||
for (int i = 0; i < num_torrents; ++i)
|
||||
{
|
||||
lt::add_torrent_params params = create_torrent(i, true);
|
||||
// torrents are paused and auto-managed
|
||||
params.flags &= ~add_torrent_params::flag_auto_managed;
|
||||
params.flags |= add_torrent_params::flag_paused;
|
||||
ses.async_add_torrent(params);
|
||||
}
|
||||
},
|
||||
|
||||
[](lt::session& ses) {
|
||||
// verify result (none should have been started)
|
||||
// make sure only 3 got started
|
||||
std::vector<lt::alert*> alerts;
|
||||
ses.pop_alerts(&alerts);
|
||||
|
||||
lt::time_point start_time = alerts[0]->timestamp();
|
||||
|
||||
for (alert* a : alerts)
|
||||
{
|
||||
fprintf(stderr, "%-3d %s\n", int(duration_cast<lt::seconds>(a->timestamp()
|
||||
- start_time).count()), a->message().c_str());
|
||||
if (state_changed_alert* sc = alert_cast<state_changed_alert>(a))
|
||||
{
|
||||
TEST_CHECK(sc->state == torrent_status::checking_files
|
||||
|| sc->state == torrent_status::checking_resume_data);
|
||||
}
|
||||
}
|
||||
|
||||
for (torrent_handle const& h : ses.get_torrents())
|
||||
{
|
||||
// even though all torrents are seeding, libtorrent shouldn't know
|
||||
// that, because they should never have been checked (because they
|
||||
// were force stopped)
|
||||
TEST_CHECK(!h.status().is_seeding);
|
||||
TEST_CHECK(!h.status().auto_managed);
|
||||
TEST_CHECK(h.status().paused);
|
||||
}
|
||||
});
|
||||
}
|
||||
// TODO: assert that the torrent_paused_alert is posted when pausing
|
||||
// downloading, seeding, checking torrents as well as the graceful pause
|
||||
// TODO: test limits of tracker, DHT and LSD announces
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ using namespace libtorrent;
|
|||
using namespace sim;
|
||||
namespace lt = libtorrent;
|
||||
|
||||
// this is a test for torrent_status time counters are correct
|
||||
struct test_swarm_config : swarm_config
|
||||
{
|
||||
test_swarm_config()
|
||||
|
|
|
@ -63,7 +63,7 @@ struct swarm_config : swarm_setup_provider
|
|||
|
||||
create_directory(save_path, ec);
|
||||
std::ofstream file(combine_path(save_path, "temporary").c_str());
|
||||
m_ti = ::create_torrent(&file, 0x4000, 90, false);
|
||||
m_ti = ::create_torrent(&file, "temporary", 0x4000, 90, false);
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
|
|
@ -3800,6 +3800,15 @@ namespace libtorrent
|
|||
peer_log(peer_log_alert::info, "GRACEFUL_PAUSE", "NO MORE DOWNLOAD");
|
||||
#endif
|
||||
disconnect(errors::torrent_paused, op_bittorrent);
|
||||
|
||||
// if this was the last connection, post the alert
|
||||
// TODO: it would be nice if none of this logic would leak outside of
|
||||
// the torrent object)
|
||||
if (t->num_peers() == 0)
|
||||
{
|
||||
if (t->alerts().should_post<torrent_paused_alert>())
|
||||
t->alerts().emplace_alert<torrent_paused_alert>(t->get_handle());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -3556,17 +3556,18 @@ retry:
|
|||
torrent* t = *i;
|
||||
|
||||
TORRENT_ASSERT(t->state() == torrent_status::checking_files);
|
||||
if (limit <= 0)
|
||||
{
|
||||
t->pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
t->resume();
|
||||
t->start_checking();
|
||||
--limit;
|
||||
}
|
||||
TORRENT_ASSERT(t->is_auto_managed());
|
||||
if (limit <= 0)
|
||||
{
|
||||
t->pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
t->resume();
|
||||
t->start_checking();
|
||||
--limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void session_impl::auto_manage_torrents(std::vector<torrent*>& list
|
||||
|
@ -3580,6 +3581,23 @@ retry:
|
|||
|
||||
TORRENT_ASSERT(t->state() != torrent_status::checking_files);
|
||||
|
||||
// inactive torrents don't count (and if you configured them to do so,
|
||||
// the torrent won't say it's inactive)
|
||||
if (hard_limit > 0 && t->is_inactive())
|
||||
{
|
||||
t->set_announce_to_dht(--dht_limit >= 0);
|
||||
t->set_announce_to_trackers(--tracker_limit >= 0);
|
||||
t->set_announce_to_lsd(--lsd_limit >= 0);
|
||||
|
||||
--hard_limit;
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
if (!t->allows_peers())
|
||||
t->log_to_all_peers("auto manager starting (inactive) torrent");
|
||||
#endif
|
||||
t->set_allow_peers(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type_limit > 0 && hard_limit > 0)
|
||||
{
|
||||
t->set_announce_to_dht(--dht_limit >= 0);
|
||||
|
@ -3593,19 +3611,18 @@ retry:
|
|||
t->log_to_all_peers("auto manager starting torrent");
|
||||
#endif
|
||||
t->set_allow_peers(true);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
if (t->allows_peers())
|
||||
t->log_to_all_peers("auto manager pausing torrent");
|
||||
if (t->allows_peers())
|
||||
t->log_to_all_peers("auto manager pausing torrent");
|
||||
#endif
|
||||
// use graceful pause for auto-managed torrents
|
||||
t->set_allow_peers(false, true);
|
||||
t->set_announce_to_dht(false);
|
||||
t->set_announce_to_trackers(false);
|
||||
t->set_announce_to_lsd(false);
|
||||
}
|
||||
// use graceful pause for auto-managed torrents
|
||||
t->set_allow_peers(false, true);
|
||||
t->set_announce_to_dht(false);
|
||||
t->set_announce_to_trackers(false);
|
||||
t->set_announce_to_lsd(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3629,18 +3646,20 @@ retry:
|
|||
|
||||
// these counters are set to the number of torrents
|
||||
// of each kind we're allowed to have active
|
||||
int num_downloaders = settings().get_int(settings_pack::active_downloads);
|
||||
int num_seeds = settings().get_int(settings_pack::active_seeds);
|
||||
int checking_limit = 1;
|
||||
int downloading_limit = settings().get_int(settings_pack::active_downloads);
|
||||
int seeding_limit = settings().get_int(settings_pack::active_seeds);
|
||||
int checking_limit = settings().get_int(settings_pack::active_checking);
|
||||
int dht_limit = settings().get_int(settings_pack::active_dht_limit);
|
||||
int tracker_limit = settings().get_int(settings_pack::active_tracker_limit);
|
||||
int lsd_limit = settings().get_int(settings_pack::active_lsd_limit);
|
||||
int hard_limit = settings().get_int(settings_pack::active_limit);
|
||||
|
||||
if (num_downloaders == -1)
|
||||
num_downloaders = (std::numeric_limits<int>::max)();
|
||||
if (num_seeds == -1)
|
||||
num_seeds = (std::numeric_limits<int>::max)();
|
||||
if (downloading_limit == -1)
|
||||
downloading_limit = (std::numeric_limits<int>::max)();
|
||||
if (seeding_limit == -1)
|
||||
seeding_limit = (std::numeric_limits<int>::max)();
|
||||
if (checking_limit == -1)
|
||||
checking_limit = (std::numeric_limits<int>::max)();
|
||||
if (hard_limit == -1)
|
||||
hard_limit = (std::numeric_limits<int>::max)();
|
||||
if (dht_limit == -1)
|
||||
|
@ -3650,20 +3669,6 @@ retry:
|
|||
if (tracker_limit == -1)
|
||||
tracker_limit = (std::numeric_limits<int>::max)();
|
||||
|
||||
// deduct "force started" torrents from the hard_limit
|
||||
// we don't have explicit access to the number of force started torrents,
|
||||
// but we know how many started downloading and seeding torrents we have.
|
||||
// if we subtract all non-force started torrents from the total, we get
|
||||
// the number of force started.
|
||||
hard_limit -= m_stats_counters[counters::num_downloading_torrents] -
|
||||
downloaders.size();
|
||||
hard_limit -= m_stats_counters[counters::num_seeding_torrents]
|
||||
+ m_stats_counters[counters::num_upload_only_torrents] -
|
||||
seeds.size();
|
||||
|
||||
// TODO: 3 also deduct force started checking torrents from checking_limit
|
||||
// also deduct started inactive torrents from hard_limit
|
||||
|
||||
// if hard_limit is <= 0, all torrents in these lists should be paused.
|
||||
// The order is not relevant
|
||||
if (hard_limit > 0)
|
||||
|
@ -3691,16 +3696,16 @@ retry:
|
|||
if (settings().get_bool(settings_pack::auto_manage_prefer_seeds))
|
||||
{
|
||||
auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit
|
||||
, hard_limit, num_seeds);
|
||||
, hard_limit, seeding_limit);
|
||||
auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit
|
||||
, hard_limit, num_downloaders);
|
||||
, hard_limit, downloading_limit);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto_manage_torrents(downloaders, dht_limit, tracker_limit, lsd_limit
|
||||
, hard_limit, num_downloaders);
|
||||
, hard_limit, downloading_limit);
|
||||
auto_manage_torrents(seeds, dht_limit, tracker_limit, lsd_limit
|
||||
, hard_limit, num_seeds);
|
||||
, hard_limit, seeding_limit);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -260,6 +260,7 @@ namespace libtorrent
|
|||
SET(peer_tos, 0, &session_impl::update_peer_tos),
|
||||
SET(active_downloads, 3, &session_impl::trigger_auto_manage),
|
||||
SET(active_seeds, 5, &session_impl::trigger_auto_manage),
|
||||
SET_NOPREV(active_checking, 1, &session_impl::trigger_auto_manage),
|
||||
SET(active_dht_limit, 88, 0),
|
||||
SET(active_tracker_limit, 1600, 0),
|
||||
SET(active_lsd_limit, 60, 0),
|
||||
|
|
144
src/torrent.cpp
144
src/torrent.cpp
|
@ -2553,6 +2553,7 @@ namespace libtorrent
|
|||
// either the fastresume data was rejected or there are
|
||||
// some files
|
||||
set_state(torrent_status::checking_files);
|
||||
if (should_check_files()) start_checking();
|
||||
|
||||
// start the checking right away (potentially)
|
||||
m_ses.trigger_auto_manage();
|
||||
|
@ -2814,20 +2815,19 @@ namespace libtorrent
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_graceful_pause_mode && !m_allow_peers && m_checking_piece == m_num_checked_pieces)
|
||||
{
|
||||
// we are in graceful pause mode, and we just completed the last outstanding job.
|
||||
// now we can be considered paused
|
||||
if (alerts().should_post<torrent_paused_alert>())
|
||||
alerts().emplace_alert<torrent_paused_alert>(get_handle());
|
||||
}
|
||||
|
||||
// we paused the checking
|
||||
if (!should_check_files())
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
debug_log("on_piece_hashed, checking paused");
|
||||
#endif
|
||||
if (m_checking_piece == m_num_checked_pieces)
|
||||
{
|
||||
// we are paused, and we just completed the last outstanding job.
|
||||
// now we can be considered paused
|
||||
if (alerts().should_post<torrent_paused_alert>())
|
||||
alerts().emplace_alert<torrent_paused_alert>(get_handle());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2853,12 +2853,15 @@ namespace libtorrent
|
|||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
debug_log("on_piece_hashed, completed");
|
||||
#endif
|
||||
// we're done checking!
|
||||
files_checked();
|
||||
if (m_auto_managed)
|
||||
{
|
||||
// if we're auto managed. assume we need to be paused until the auto
|
||||
// managed logic runs again (which is triggered further down)
|
||||
pause();
|
||||
}
|
||||
|
||||
// recalculate auto-managed torrents sooner
|
||||
// in order to start checking the next torrent
|
||||
m_ses.trigger_auto_manage();
|
||||
// we're done checking! (this should cause a call to trigger_auto_manage)
|
||||
files_checked();
|
||||
|
||||
// reset the checking state
|
||||
m_checking_piece = 0;
|
||||
|
@ -8082,23 +8085,27 @@ namespace libtorrent
|
|||
bool is_downloading = false;
|
||||
bool is_seeding = false;
|
||||
|
||||
if (m_state == torrent_status::checking_files
|
||||
&& (is_auto_managed() || allows_peers()))
|
||||
if (is_auto_managed() && !has_error())
|
||||
{
|
||||
is_checking = true;
|
||||
}
|
||||
else if (is_auto_managed() && !has_error()
|
||||
&& (is_paused()
|
||||
|| !is_inactive()
|
||||
|| !settings().get_bool(settings_pack::dont_count_slow_torrents)))
|
||||
{
|
||||
// torrents that are started (not paused) and
|
||||
// inactive are not part of any list. They will not be touched because
|
||||
// they are inactive
|
||||
if (is_finished())
|
||||
is_seeding = true;
|
||||
else
|
||||
is_downloading = true;
|
||||
if (m_state == torrent_status::checking_files
|
||||
|| m_state == torrent_status::allocating)
|
||||
{
|
||||
is_checking = true;
|
||||
}
|
||||
else if (m_state == torrent_status::downloading_metadata
|
||||
|| m_state == torrent_status::downloading
|
||||
|| m_state == torrent_status::finished
|
||||
|| m_state == torrent_status::seeding
|
||||
|| m_state == torrent_status::downloading)
|
||||
{
|
||||
// torrents that are started (not paused) and
|
||||
// inactive are not part of any list. They will not be touched because
|
||||
// they are inactive
|
||||
if (is_finished())
|
||||
is_seeding = true;
|
||||
else
|
||||
is_downloading = true;
|
||||
}
|
||||
}
|
||||
|
||||
update_list(aux::session_interface::torrent_downloading_auto_managed
|
||||
|
@ -8550,10 +8557,6 @@ namespace libtorrent
|
|||
m_need_save_resume_data = true;
|
||||
}
|
||||
|
||||
// if we just finished checking and we're not a seed, we are
|
||||
// likely to be unpaused
|
||||
m_ses.trigger_auto_manage();
|
||||
|
||||
if (is_finished() && m_state != torrent_status::finished)
|
||||
finished();
|
||||
}
|
||||
|
@ -8825,20 +8828,24 @@ namespace libtorrent
|
|||
bool is_downloading = false;
|
||||
bool is_seeding = false;
|
||||
|
||||
if (m_state == torrent_status::checking_files
|
||||
&& (is_auto_managed() || allows_peers()))
|
||||
if (is_auto_managed() && !has_error())
|
||||
{
|
||||
is_checking = true;
|
||||
}
|
||||
else if (is_auto_managed() && !has_error()
|
||||
&& (is_paused()
|
||||
|| !is_inactive()
|
||||
|| !settings().get_bool(settings_pack::dont_count_slow_torrents)))
|
||||
{
|
||||
if (is_finished())
|
||||
is_seeding = true;
|
||||
else
|
||||
is_downloading = true;
|
||||
if (m_state == torrent_status::checking_files
|
||||
|| m_state == torrent_status::allocating)
|
||||
{
|
||||
is_checking = true;
|
||||
}
|
||||
else if (m_state == torrent_status::downloading_metadata
|
||||
|| m_state == torrent_status::downloading
|
||||
|| m_state == torrent_status::finished
|
||||
|| m_state == torrent_status::seeding
|
||||
|| m_state == torrent_status::downloading)
|
||||
{
|
||||
if (is_finished())
|
||||
is_seeding = true;
|
||||
else
|
||||
is_downloading = true;
|
||||
}
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(m_links[aux::session_interface::torrent_checking_auto_managed].in_list()
|
||||
|
@ -9359,9 +9366,9 @@ namespace libtorrent
|
|||
enum flags
|
||||
{
|
||||
seed_ratio_not_met = 0x40000000,
|
||||
no_seeds = 0x20000000,
|
||||
recently_started = 0x10000000,
|
||||
prio_mask = 0x0fffffff
|
||||
no_seeds = 0x20000000,
|
||||
recently_started = 0x10000000,
|
||||
prio_mask = 0x0fffffff
|
||||
};
|
||||
|
||||
if (!is_finished()) return 0;
|
||||
|
@ -9556,17 +9563,8 @@ namespace libtorrent
|
|||
m_need_save_resume_data = true;
|
||||
state_updated();
|
||||
|
||||
bool prev_graceful = m_graceful_pause_mode;
|
||||
m_graceful_pause_mode = graceful;
|
||||
update_gauge();
|
||||
|
||||
if (!m_ses.is_paused() || (prev_graceful && !m_graceful_pause_mode))
|
||||
{
|
||||
do_pause();
|
||||
// if this torrent was just paused
|
||||
// we might have to resume some other auto-managed torrent
|
||||
m_ses.trigger_auto_manage();
|
||||
}
|
||||
}
|
||||
|
||||
void torrent::do_pause()
|
||||
|
@ -9675,6 +9673,11 @@ namespace libtorrent
|
|||
p->disconnect(errors::torrent_paused, op_bittorrent);
|
||||
i = j;
|
||||
}
|
||||
if (m_connections.empty())
|
||||
{
|
||||
if (alerts().should_post<torrent_paused_alert>())
|
||||
alerts().emplace_alert<torrent_paused_alert>(get_handle());
|
||||
}
|
||||
if (update_ticks)
|
||||
{
|
||||
update_want_peers();
|
||||
|
@ -9730,8 +9733,20 @@ namespace libtorrent
|
|||
{
|
||||
TORRENT_ASSERT(is_single_thread());
|
||||
|
||||
if (m_allow_peers == b
|
||||
&& m_graceful_pause_mode == graceful) return;
|
||||
if (m_allow_peers == b)
|
||||
{
|
||||
// there is one special case here. If we are
|
||||
// currently in graceful pause mode, and we just turned into regular
|
||||
// paused mode, we need to actually pause the torrent properly
|
||||
if (m_allow_peers == false
|
||||
&& m_graceful_pause_mode == true
|
||||
&& graceful == false)
|
||||
{
|
||||
m_graceful_pause_mode = graceful;
|
||||
do_pause();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_allow_peers = b;
|
||||
if (!m_ses.is_paused())
|
||||
|
@ -9974,18 +9989,27 @@ namespace libtorrent
|
|||
|
||||
int torrent::finished_time() const
|
||||
{
|
||||
// m_finished_time does not account for the current "session", just the
|
||||
// time before we last started this torrent. To get the current time, we
|
||||
// need to add the time since we started it
|
||||
return m_finished_time + ((!is_finished() || is_paused()) ? 0
|
||||
: (m_ses.session_time() - m_became_finished));
|
||||
}
|
||||
|
||||
int torrent::active_time() const
|
||||
{
|
||||
// m_active_time does not account for the current "session", just the
|
||||
// time before we last started this torrent. To get the current time, we
|
||||
// need to add the time since we started it
|
||||
return m_active_time + (is_paused() ? 0
|
||||
: m_ses.session_time() - m_started);
|
||||
}
|
||||
|
||||
int torrent::seeding_time() const
|
||||
{
|
||||
// m_seeding_time does not account for the current "session", just the
|
||||
// time before we last started this torrent. To get the current time, we
|
||||
// need to add the time since we started it
|
||||
return m_seeding_time + ((!is_seed() || is_paused()) ? 0
|
||||
: m_ses.session_time() - m_became_seed);
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ using namespace libtorrent;
|
|||
int old_stdout = -1;
|
||||
int old_stderr = -1;
|
||||
bool redirect_output = true;
|
||||
bool keep_files = false;
|
||||
|
||||
extern int _g_test_idx;
|
||||
|
||||
|
@ -143,6 +144,8 @@ void print_usage(char const* executable)
|
|||
"OPTIONS:\n"
|
||||
"-h,--help show this help\n"
|
||||
"-l,--list list the tests available to run\n"
|
||||
"-k,--keep keep files created by the test\n"
|
||||
" regardless of whether it passed or not\n"
|
||||
"-n,--no-redirect don't redirect test output to\n"
|
||||
" temporary file, but let it go straight\n"
|
||||
" to stdout\n"
|
||||
|
@ -181,6 +184,11 @@ EXPORT int main(int argc, char const* argv[])
|
|||
{
|
||||
redirect_output = false;
|
||||
}
|
||||
|
||||
if (strcmp(argv[0], "-k") == 0 || strcmp(argv[0], "--keep") == 0)
|
||||
{
|
||||
keep_files = true;
|
||||
}
|
||||
++argv;
|
||||
--argc;
|
||||
}
|
||||
|
@ -365,7 +373,7 @@ EXPORT int main(int argc, char const* argv[])
|
|||
|
||||
int ret = print_failures();
|
||||
#if !defined TORRENT_LOGGING
|
||||
if (ret == 0)
|
||||
if (ret == 0 && !keep_files)
|
||||
{
|
||||
remove_all(test_dir, ec);
|
||||
if (ec)
|
||||
|
|
|
@ -615,7 +615,8 @@ void create_random_files(std::string const& path, const int file_sizes[], int nu
|
|||
free(random_data);
|
||||
}
|
||||
|
||||
boost::shared_ptr<torrent_info> create_torrent(std::ostream* file, int piece_size
|
||||
boost::shared_ptr<torrent_info> create_torrent(std::ostream* file
|
||||
, char const* name, int piece_size
|
||||
, int num_pieces, bool add_tracker, std::string ssl_certificate)
|
||||
{
|
||||
// excercise the path when encountering invalid urls
|
||||
|
@ -624,7 +625,7 @@ boost::shared_ptr<torrent_info> create_torrent(std::ostream* file, int piece_siz
|
|||
|
||||
file_storage fs;
|
||||
int total_size = piece_size * num_pieces;
|
||||
fs.add_file("temporary", total_size);
|
||||
fs.add_file(name, total_size);
|
||||
libtorrent::create_torrent t(fs, piece_size);
|
||||
if (add_tracker)
|
||||
{
|
||||
|
@ -744,7 +745,7 @@ setup_transfer(lt::session* ses1, lt::session* ses2, lt::session* ses3
|
|||
error_code ec;
|
||||
create_directory("tmp1" + suffix, ec);
|
||||
std::ofstream file(combine_path("tmp1" + suffix, "temporary").c_str());
|
||||
t = ::create_torrent(&file, piece_size, 9, false);
|
||||
t = ::create_torrent(&file, "temporary", piece_size, 9, false);
|
||||
file.close();
|
||||
if (clear_files)
|
||||
{
|
||||
|
|
|
@ -83,8 +83,8 @@ EXPORT void test_sleep(int millisec);
|
|||
EXPORT void create_random_files(std::string const& path, const int file_sizes[], int num_files);
|
||||
|
||||
EXPORT boost::shared_ptr<libtorrent::torrent_info> create_torrent(std::ostream* file = 0
|
||||
, int piece_size = 16 * 1024, int num_pieces = 13, bool add_tracker = true
|
||||
, std::string ssl_certificate = "");
|
||||
, char const* name = "temporary", int piece_size = 16 * 1024, int num_pieces = 13
|
||||
, bool add_tracker = true, std::string ssl_certificate = "");
|
||||
|
||||
EXPORT boost::tuple<libtorrent::torrent_handle
|
||||
, libtorrent::torrent_handle
|
||||
|
|
|
@ -119,7 +119,7 @@ void test_transfer(settings_pack const& sett)
|
|||
error_code ec;
|
||||
create_directory("tmp1_priority", ec);
|
||||
std::ofstream file("tmp1_priority/temporary");
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false);
|
||||
file.close();
|
||||
|
||||
add_torrent_params addp;
|
||||
|
|
|
@ -139,7 +139,7 @@ session_proxy test_proxy(settings_pack::proxy_type_t proxy_type, int flags)
|
|||
remove_all("tmp1_privacy", ec);
|
||||
create_directory("tmp1_privacy", ec);
|
||||
std::ofstream file(combine_path("tmp1_privacy", "temporary").c_str());
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false);
|
||||
file.close();
|
||||
|
||||
char http_tracker_url[200];
|
||||
|
|
|
@ -80,7 +80,7 @@ TORRENT_TEST(recheck)
|
|||
create_directory("tmp1_recheck", ec);
|
||||
if (ec) fprintf(stderr, "create_directory: %s\n", ec.message().c_str());
|
||||
std::ofstream file("tmp1_recheck/temporary");
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 4 * 1024 * 1024
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 4 * 1024 * 1024
|
||||
, 7, false);
|
||||
file.close();
|
||||
|
||||
|
|
|
@ -256,7 +256,7 @@ void test_remap_files_scatter(storage_mode_t storage_mode = storage_mode_sparse)
|
|||
|
||||
create_directory("tmp1_remap2", ec);
|
||||
std::ofstream file("tmp1_remap2/temporary");
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 32 * 1024, 7);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 32 * 1024, 7);
|
||||
file.close();
|
||||
|
||||
file_storage fs;
|
||||
|
|
|
@ -160,7 +160,7 @@ void test_ssl(int test_idx, bool use_utp)
|
|||
|
||||
create_directory("tmp1_ssl", ec);
|
||||
std::ofstream file("tmp1_ssl/temporary");
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary"
|
||||
, 16 * 1024, 13, false, combine_path("..", combine_path("ssl", "root_ca_cert.pem")));
|
||||
file.close();
|
||||
|
||||
|
@ -549,7 +549,7 @@ void test_malicious_peer()
|
|||
// create torrent
|
||||
create_directory("tmp3_ssl", ec);
|
||||
std::ofstream file("tmp3_ssl/temporary");
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary"
|
||||
, 16 * 1024, 13, false, combine_path("..", combine_path("ssl", "root_ca_cert.pem")));
|
||||
file.close();
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ TORRENT_TEST(udp_tracker)
|
|||
remove_all("tmp1_tracker", ec);
|
||||
create_directory("tmp1_tracker", ec);
|
||||
std::ofstream file(combine_path("tmp1_tracker", "temporary").c_str());
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false);
|
||||
file.close();
|
||||
|
||||
char tracker_url[200];
|
||||
|
@ -411,7 +411,7 @@ TORRENT_TEST(try_next)
|
|||
remove_all("tmp2_tracker", ec);
|
||||
create_directory("tmp2_tracker", ec);
|
||||
std::ofstream file(combine_path("tmp2_tracker", "temporary").c_str());
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false);
|
||||
file.close();
|
||||
|
||||
// this should fail
|
||||
|
@ -519,7 +519,7 @@ TORRENT_TEST(http_peers)
|
|||
remove_all("tmp2_tracker", ec);
|
||||
create_directory("tmp2_tracker", ec);
|
||||
std::ofstream file(combine_path("tmp2_tracker", "temporary").c_str());
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false);
|
||||
file.close();
|
||||
|
||||
char tracker_url[200];
|
||||
|
@ -593,7 +593,7 @@ void test_proxy(bool proxy_trackers)
|
|||
remove_all("tmp2_tracker", ec);
|
||||
create_directory("tmp2_tracker", ec);
|
||||
std::ofstream file(combine_path("tmp2_tracker", "temporary").c_str());
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false);
|
||||
file.close();
|
||||
|
||||
char tracker_url[200];
|
||||
|
|
|
@ -235,7 +235,7 @@ void test_transfer(int proxy_type, settings_pack const& sett
|
|||
|
||||
create_directory("tmp1_transfer", ec);
|
||||
std::ofstream file("tmp1_transfer/temporary");
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 16 * 1024, 13, false);
|
||||
file.close();
|
||||
|
||||
TEST_CHECK(exists(combine_path("tmp1_transfer", "temporary")));
|
||||
|
|
|
@ -93,7 +93,7 @@ void test_transfer()
|
|||
|
||||
create_directory("./tmp1_utp", ec);
|
||||
std::ofstream file("./tmp1_utp/temporary");
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, 128 * 1024, 6, false);
|
||||
boost::shared_ptr<torrent_info> t = ::create_torrent(&file, "temporary", 128 * 1024, 6, false);
|
||||
file.close();
|
||||
|
||||
// for performance testing
|
||||
|
|
Loading…
Reference in New Issue