diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 7ebb5d440..24f3e0c6e 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -369,7 +369,7 @@ namespace libtorrent , int protocol, error_code const& ec, int nat_transport); bool is_aborted() const override { return m_abort; } - bool is_paused() const override { return m_paused; } + bool is_paused() const { return m_paused; } void pause(); void resume(); diff --git a/include/libtorrent/aux_/session_interface.hpp b/include/libtorrent/aux_/session_interface.hpp index d39a52954..768b9a369 100644 --- a/include/libtorrent/aux_/session_interface.hpp +++ b/include/libtorrent/aux_/session_interface.hpp @@ -176,7 +176,6 @@ namespace libtorrent { namespace aux virtual boost::uint16_t session_time() const = 0; - virtual bool is_paused() const = 0; virtual bool is_aborted() const = 0; virtual int num_uploads() const = 0; virtual bool preemptive_unchoke() const = 0; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index ba710edc6..e43495ab4 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -173,7 +173,7 @@ namespace libtorrent struct TORRENT_EXTRA_EXPORT torrent_hot_members { torrent_hot_members(aux::session_interface& ses - , add_torrent_params const& p, int block_size); + , add_torrent_params const& p, int block_size, bool session_paused); protected: // the piece picker. This is allocated lazily. When we don't @@ -225,6 +225,10 @@ namespace libtorrent // is true if this torrent has allows having peers bool m_paused:1; + // is true if the session is paused, in which case the torrent is + // effectively paused as well. + bool m_session_paused:1; + // this is set when the torrent is in share-mode bool m_share_mode:1; @@ -274,7 +278,7 @@ namespace libtorrent public: torrent(aux::session_interface& ses, int block_size - , int seq, add_torrent_params const& p + , int seq, bool session_paused, add_torrent_params const& p , sha1_hash const& info_hash); ~torrent(); @@ -458,6 +462,7 @@ namespace libtorrent flag_graceful_pause = 1, flag_clear_disk_cache = 2 }; + void set_session_paused(bool b); void set_paused(bool b, int flags = flag_clear_disk_cache); void set_announce_to_dht(bool b) { m_announce_to_dht = b; } void set_announce_to_trackers(bool b) { m_announce_to_trackers = b; } diff --git a/simulation/Jamfile b/simulation/Jamfile index 9f8f21c20..744ca5762 100644 --- a/simulation/Jamfile +++ b/simulation/Jamfile @@ -24,6 +24,7 @@ project ; alias libtorrent-sims : + [ run test_pause.cpp ] [ run test_socks5.cpp ] [ run test_checking.cpp ] [ run test_optimistic_unchoke.cpp ] diff --git a/simulation/fake_peer.hpp b/simulation/fake_peer.hpp index 9717ee6cf..ba9cb0230 100644 --- a/simulation/fake_peer.hpp +++ b/simulation/fake_peer.hpp @@ -53,9 +53,11 @@ struct fake_peer fake_peer(simulation& sim, char const* ip) : m_ios(sim, asio::ip::address::from_string(ip)) , m_acceptor(m_ios) - , m_in_socket(m_ios) - , m_out_socket(m_ios) - , m_tripped(false) + , m_socket(m_ios) + , m_info_hash(0) + , m_accepted(false) + , m_connected(false) + , m_disconnected(false) { boost::system::error_code ec; m_acceptor.open(asio::ip::tcp::v4(), ec); @@ -65,37 +67,46 @@ struct fake_peer m_acceptor.listen(10, ec); TEST_CHECK(!ec); - m_acceptor.async_accept(m_in_socket, [&] (boost::system::error_code const& ec) + m_acceptor.async_accept(m_socket, [&] (boost::system::error_code const& ec) { - // TODO: ideally we would kick off a read on the socket to verify that - // we received a bittorrent handshake - if (!ec) m_tripped = true; + using namespace std::placeholders; + if (ec) return; + + asio::async_read(m_socket, asio::mutable_buffers_1(&m_out_buffer[0], 68) + , std::bind(&fake_peer::read_handshake, this, _1, _2)); + + m_accepted = true; }); } void close() { m_acceptor.close(); - m_in_socket.close(); - m_out_socket.close(); + m_socket.close(); } void connect_to(asio::ip::tcp::endpoint ep, lt::sha1_hash const& ih) { using namespace std::placeholders; + m_info_hash = ih; + boost::system::error_code ec; - m_out_socket.async_connect(ep, std::bind(&fake_peer::write_handshake + m_socket.async_connect(ep, std::bind(&fake_peer::write_handshake , this, _1, ih)); } - bool tripped() const { return m_tripped; } + bool accepted() const { return m_accepted; } + bool connected() const { return m_connected; } + bool disconnected() const { return m_disconnected; } private: void write_handshake(boost::system::error_code const& ec, lt::sha1_hash ih) { - asio::ip::tcp::endpoint ep = m_out_socket.remote_endpoint(); + using namespace std::placeholders; + + asio::ip::tcp::endpoint ep = m_socket.remote_endpoint(); std::printf("fake_peer::connect (%s) -> (%d) %s\n" , lt::print_endpoint(ep).c_str(), ec.value() , ec.message().c_str()); @@ -108,43 +119,140 @@ private: memcpy(m_out_buffer, handshake, len); memcpy(&m_out_buffer[28], ih.data(), 20); - asio::async_write(m_out_socket, asio::const_buffers_1(&m_out_buffer[0] - , len), [=](boost::system::error_code const& ec, size_t bytes_transferred) + asio::async_write(m_socket, asio::const_buffers_1(&m_out_buffer[0] + , len), [this, ep](boost::system::error_code const& ec, size_t bytes_transferred) { std::printf("fake_peer::write_handshake(%s) -> (%d) %s\n" , lt::print_endpoint(ep).c_str(), ec.value() , ec.message().c_str()); - this->m_out_socket.close(); + asio::async_read(m_socket, asio::mutable_buffers_1(&m_out_buffer[0], 68) + , std::bind(&fake_peer::read_handshake, this, _1, _2)); }); } + void read_handshake(lt::error_code const& ec, size_t bytes_transferred) + { + using namespace std::placeholders; + + std::printf("fake_peer::read_handshake -> (%d) %s\n" + , ec.value(), ec.message().c_str()); + if (ec) + { + m_socket.close(); + return; + } + + if (memcmp(&m_out_buffer[0], "\x13" "BitTorrent protocol", 20) != 0) + { + std::printf(" invalid protocol specifier\n"); + m_socket.close(); + return; + } + + // if this peer accepted an incoming connection, we don't know what the + // info hash is supposed to be + if (!m_info_hash.is_all_zeros() + && memcmp(&m_out_buffer[28], m_info_hash.data(), 20) != 0) + { + std::printf(" invalid info hash\n"); + m_socket.close(); + return; + } + + m_connected = true; + + // keep reading until we receie EOF, then set m_disconnected = true + m_socket.async_read_some(asio::mutable_buffers_1(&m_out_buffer[0] + , sizeof(m_out_buffer)) + , std::bind(&fake_peer::on_read, this, _1, _2)); + } + + void on_read(lt::error_code const& ec, size_t bytes_transferred) + { + using namespace std::placeholders; + + std::printf("fake_peer::on_read -> (%d) %s\n" + , ec.value(), ec.message().c_str()); + if (ec) + { + std::printf(" closing\n"); + m_disconnected = true; + m_socket.close(); + return; + } + + m_socket.async_read_some(asio::mutable_buffers_1(&m_out_buffer[0] + , sizeof(m_out_buffer)) + , std::bind(&fake_peer::on_read, this, _1, _2)); + } + char m_out_buffer[300]; asio::io_service m_ios; asio::ip::tcp::acceptor m_acceptor; - asio::ip::tcp::socket m_in_socket; - asio::ip::tcp::socket m_out_socket; - bool m_tripped; + asio::ip::tcp::socket m_socket; + lt::sha1_hash m_info_hash; + + // set to true if this peer received an incoming connection + // if this is an outgoing connection, this will always be false + bool m_accepted; + + // set to true if this peer completed a bittorrent handshake + bool m_connected; + + // set to true if this peer has been disconnected by the other end + bool m_disconnected; }; -inline void add_fake_peers(lt::torrent_handle h) +inline void add_fake_peer(lt::torrent_handle& h, int const i) +{ + char ep[30]; + std::snprintf(ep, sizeof(ep), "60.0.0.%d", i); + h.connect_peer(lt::tcp::endpoint( + lt::address_v4::from_string(ep), 6881)); +} + +inline void add_fake_peers(lt::torrent_handle& h, int const n = 5) { // add the fake peers - for (int i = 0; i < 5; ++i) + for (int i = 0; i < n; ++i) { - char ep[30]; - std::snprintf(ep, sizeof(ep), "60.0.0.%d", i); - h.connect_peer(lt::tcp::endpoint( - lt::address_v4::from_string(ep), 6881)); + add_fake_peer(h, i); } } -inline void check_tripped(std::array& test_peers, std::array expected) +template +void check_accepted(std::array& test_peers + , std::array expected) { int idx = 0; for (auto p : test_peers) { - TEST_EQUAL(p->tripped(), expected[idx]); + TEST_EQUAL(p->accepted(), expected[idx]); + ++idx; + } +} + +template +void check_connected(std::array& test_peers + , std::array expected) +{ + int idx = 0; + for (auto p : test_peers) + { + TEST_EQUAL(p->connected(), expected[idx]); + ++idx; + } +} + +template +void check_disconnected(std::array& test_peers + , std::array expected) +{ + int idx = 0; + for (auto p : test_peers) + { + TEST_EQUAL(p->disconnected(), expected[idx]); ++idx; } } diff --git a/simulation/test_auto_manage.cpp b/simulation/test_auto_manage.cpp index 00fb32de7..c897e896e 100644 --- a/simulation/test_auto_manage.cpp +++ b/simulation/test_auto_manage.cpp @@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert_types.hpp" #include "libtorrent/deadline_timer.hpp" #include "settings.hpp" +#include "utils.hpp" #include "create_torrent.hpp" #include "simulator/simulator.hpp" #include "simulator/utils.hpp" @@ -49,14 +50,6 @@ namespace lt = libtorrent; using sim::asio::ip::address_v4; -std::unique_ptr make_io_service(sim::simulation& sim, int i) -{ - char ep[30]; - std::snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff); - return std::unique_ptr(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) diff --git a/simulation/test_ip_filter.cpp b/simulation/test_ip_filter.cpp index 576dcb7d7..217788c44 100644 --- a/simulation/test_ip_filter.cpp +++ b/simulation/test_ip_filter.cpp @@ -137,7 +137,7 @@ TORRENT_TEST(apply_ip_filter) [](lt::session& ses, std::array& test_peers) { - check_tripped(test_peers, {{false, false, false, true, true}} ); + check_accepted(test_peers, {{false, false, false, true, true}} ); } ); } @@ -170,7 +170,7 @@ TORRENT_TEST(update_ip_filter) [](lt::session& ses, std::array& test_peers) { - check_tripped(test_peers, {{false, false, false, true, true}} ); + check_accepted(test_peers, {{false, false, false, true, true}} ); } ); } @@ -204,7 +204,7 @@ TORRENT_TEST(apply_ip_filter_to_torrent) { // since the IP filter didn't apply to this torrent, it should have hit // all peers - check_tripped(test_peers, {{true, true, true, true, true}} ); + check_accepted(test_peers, {{true, true, true, true, true}} ); } ); } @@ -233,7 +233,7 @@ TORRENT_TEST(ip_filter_trackers) [](lt::session& ses, lt::alert const* a) {}, [](lt::session& ses, std::array& test_peers) { - check_tripped(test_peers, {{false, false, false, true, true}} ); + check_accepted(test_peers, {{false, false, false, true, true}} ); } ); } diff --git a/simulation/test_pause.cpp b/simulation/test_pause.cpp new file mode 100644 index 000000000..fa0dd9102 --- /dev/null +++ b/simulation/test_pause.cpp @@ -0,0 +1,337 @@ +/* + +Copyright (c) 2016, 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 "settings.hpp" +#include "fake_peer.hpp" +#include "utils.hpp" +#include "create_torrent.hpp" +#include "simulator/simulator.hpp" +#include "simulator/utils.hpp" +#include + +using namespace sim; +using namespace libtorrent; + +namespace lt = libtorrent; + +using sim::asio::ip::address_v4; + +// 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 +void run_test(Setup const& setup, Torrent const& torrent + , Test const& test, Check const& check) +{ + // setup the simulation + sim::default_config network_cfg; + sim::simulation sim{network_cfg}; + std::unique_ptr 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(); + + // create session + std::shared_ptr ses = std::make_shared(pack, *ios); + + setup(*ses); + + fake_peer p1(sim, "60.0.0.0"); + fake_peer p2(sim, "60.0.0.1"); + fake_peer p3(sim, "60.0.0.2"); + std::array test_peers = {{ &p1, &p2, &p3 }}; + + // add torrent + lt::add_torrent_params params = create_torrent(0, false); + params.flags &= ~lt::add_torrent_params::flag_auto_managed; + params.flags &= ~lt::add_torrent_params::flag_paused; + ses->async_add_torrent(params); + + lt::torrent_handle h; + print_alerts(*ses, [&](lt::session& ses, lt::alert const* a) { + auto at = lt::alert_cast(a); + if (at == nullptr) return; + h = at->handle; + + // disable the print_alert object from polling any more alerts + ses.set_alert_notify([]{}); + + torrent(ses, h, test_peers); + }); + + sim::timer t1(sim, lt::seconds(5) + , [&](boost::system::error_code const& ec) + { + test(*ses, h, test_peers); + }); + + // set up a timer to fire later, to verify everything we expected to happen + // happened + sim::timer t2(sim, lt::seconds(10) + , [&](boost::system::error_code const& ec) + { + check(*ses, h, test_peers); + + // shut down + zombie = ses->abort(); + ses.reset(); + }); + + sim.run(); +} + +// make sure the torrent disconnects all its peers when it's paused +TORRENT_TEST(torrent_paused_disconnect) +{ + run_test( + [](lt::session& ses) {}, + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + add_fake_peers(h, 3); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + check_accepted(test_peers, {{true, true, true}}); + check_connected(test_peers, {{true, true, true}}); + check_disconnected(test_peers, {{false, false, false}}); + h.pause(); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + check_disconnected(test_peers, {{true, true, true}}); + TEST_EQUAL(h.status().paused, true); + }); +} + +// make sure the torrent disconnects all its peers when the session is paused +TORRENT_TEST(session_paused_disconnect) +{ + run_test( + [](lt::session& ses) {}, + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + add_fake_peers(h, 3); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + check_accepted(test_peers, {{true, true, true}}); + check_connected(test_peers, {{true, true, true}}); + check_disconnected(test_peers, {{false, false, false}}); + ses.pause(); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + check_disconnected(test_peers, {{true, true, true}}); + + // the torrent isn't paused, the session is + TEST_EQUAL(h.status().paused, false); + }); +} + +// make sure a torrent is not connecting to any peers when added to a paused +// session +TORRENT_TEST(paused_session_add_torrent) +{ + run_test( + [](lt::session& ses) { ses.pause(); }, + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + add_fake_peers(h, 3); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + check_accepted(test_peers, {{false, false, false}}); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + // the torrent isn't paused, the session is + TEST_EQUAL(h.status().paused, false); + }); +} + +// make sure the torrent isn't connecting to peers when it's paused +TORRENT_TEST(paused_torrent_add_peers) +{ + run_test( + [](lt::session& ses) {}, + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + h.pause(); + + add_fake_peers(h, 3); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + check_accepted(test_peers, {{false, false, false}}); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, true); + }); +} + +// make sure we post the torrent_paused alert when pausing a torrent +TORRENT_TEST(torrent_paused_alert) +{ + run_test( + [](lt::session& ses) {}, + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) {}, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + h.pause(); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, true); + + std::vector alerts; + ses.pop_alerts(&alerts); + + lt::time_point start_time = alerts[0]->timestamp(); + + int num_resume = 0; + int num_paused = 0; + for (alert* a : alerts) + { + std::printf("%-3d %s\n", int(duration_cast(a->timestamp() + - start_time).count()), a->message().c_str()); + if (lt::alert_cast(a)) ++num_resume; + if (lt::alert_cast(a)) ++num_paused; + } + + TEST_EQUAL(num_resume, 0); + TEST_EQUAL(num_paused, 1); + }); +} + +// make sure we post the torrent_paused alert when pausing the session +TORRENT_TEST(session_paused_alert) +{ + run_test( + [](lt::session& ses) {}, + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) {}, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + ses.pause(); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + + std::vector alerts; + ses.pop_alerts(&alerts); + + lt::time_point start_time = alerts[0]->timestamp(); + + int num_resume = 0; + int num_paused = 0; + for (alert* a : alerts) + { + std::printf("%-3d %s\n", int(duration_cast(a->timestamp() + - start_time).count()), a->message().c_str()); + if (lt::alert_cast(a)) ++num_resume; + if (lt::alert_cast(a)) ++num_paused; + } + + TEST_EQUAL(num_resume, 0); + TEST_EQUAL(num_paused, 1); + }); +} + +// make sure we post both the paused and resumed alert when pausing and resuming +// the session. +TORRENT_TEST(session_pause_resume) +{ + run_test( + [](lt::session& ses) {}, + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + ses.pause(); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + ses.resume(); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + + std::vector alerts; + ses.pop_alerts(&alerts); + + lt::time_point start_time = alerts[0]->timestamp(); + + int num_resume = 0; + int num_paused = 0; + for (alert* a : alerts) + { + std::printf("%-3d %s\n", int(duration_cast(a->timestamp() + - start_time).count()), a->message().c_str()); + if (lt::alert_cast(a)) ++num_resume; + if (lt::alert_cast(a)) ++num_paused; + } + + TEST_EQUAL(num_resume, 1); + TEST_EQUAL(num_paused, 1); + }); +} + +// make sure peers added to a (non-paused) torrent in a paused session are +// connected once the session is resumed +TORRENT_TEST(session_pause_resume_connect) +{ + run_test( + [](lt::session& ses) {}, + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + ses.pause(); + add_fake_peers(h, 3); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + check_accepted(test_peers, {{false, false, false}}); + ses.resume(); + }, + + [](lt::session& ses, lt::torrent_handle h, std::array& test_peers) { + TEST_EQUAL(h.status().paused, false); + + check_accepted(test_peers, {{true, true, true}}); + }); +} + diff --git a/simulation/test_socks5.cpp b/simulation/test_socks5.cpp index e37ca45f0..c59770d6b 100644 --- a/simulation/test_socks5.cpp +++ b/simulation/test_socks5.cpp @@ -51,14 +51,6 @@ using namespace libtorrent; namespace lt = libtorrent; -std::unique_ptr make_io_service(sim::simulation& sim, int i) -{ - char ep[30]; - std::snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff); - return std::unique_ptr(new sim::asio::io_service( - sim, 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) diff --git a/simulation/utils.cpp b/simulation/utils.cpp index 11a32452b..e5368380c 100644 --- a/simulation/utils.cpp +++ b/simulation/utils.cpp @@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert_types.hpp" #include "libtorrent/session_stats.hpp" #include "libtorrent/alert.hpp" +#include "libtorrent/io_service.hpp" #include "setup_swarm.hpp" using namespace libtorrent; @@ -142,3 +143,11 @@ void print_alerts(lt::session& ses } ); } ); } +std::unique_ptr make_io_service(sim::simulation& sim, int i) +{ + char ep[30]; + std::snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff); + return std::unique_ptr(new sim::asio::io_service( + sim, lt::address_v4::from_string(ep))); +} + diff --git a/simulation/utils.hpp b/simulation/utils.hpp index e46391d32..644550d5d 100644 --- a/simulation/utils.hpp +++ b/simulation/utils.hpp @@ -50,6 +50,9 @@ void filter_ips(lt::session& ses); void set_cache_size(lt::session& ses, int val); int get_cache_size(lt::session& ses); +std::unique_ptr make_io_service( + sim::simulation& sim, int i); + enum flags_t { ipv6 = 1, diff --git a/src/session_impl.cpp b/src/session_impl.cpp index dde713544..020f0e05c 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -851,11 +851,9 @@ namespace aux { session_log(" *** session paused ***"); #endif m_paused = true; - for (torrent_map::iterator i = m_torrents.begin() - , end(m_torrents.end()); i != end; ++i) + for (auto& te : m_torrents) { - torrent& t = *i->second; - t.do_pause(); + te.second->set_session_paused(true); } } @@ -865,12 +863,10 @@ namespace aux { if (!m_paused) return; m_paused = false; - for (torrent_map::iterator i = m_torrents.begin() - , end(m_torrents.end()); i != end; ++i) + + for (auto& te : m_torrents) { - torrent& t = *i->second; - t.do_resume(); - if (t.should_check_files()) t.start_checking(); + te.second->set_session_paused(false); } } @@ -3293,7 +3289,7 @@ namespace aux { // scrape paused torrents that are auto managed // (unless the session is paused) // -------------------------------------------------------------- - if (!is_paused()) + if (!m_paused) { INVARIANT_CHECK; --m_auto_scrape_time_scaler; @@ -3663,8 +3659,6 @@ namespace aux { t->log_to_all_peers("auto manager starting (inactive) torrent"); #endif t->set_paused(false); - t->update_gauge(); - t->update_want_peers(); continue; } @@ -3681,8 +3675,6 @@ namespace aux { t->log_to_all_peers("auto manager starting torrent"); #endif t->set_paused(false); - t->update_gauge(); - t->update_want_peers(); continue; } @@ -3696,8 +3688,6 @@ namespace aux { t->set_announce_to_dht(false); t->set_announce_to_trackers(false); t->set_announce_to_lsd(false); - t->update_gauge(); - t->update_want_peers(); } } @@ -3715,7 +3705,7 @@ namespace aux { m_last_auto_manage = time_now(); m_need_auto_manage = false; - if (is_paused()) return; + if (m_paused) return; // make copies of the lists of torrents that we want to consider for auto // management. We need copies because they will be sorted. @@ -4908,7 +4898,8 @@ namespace aux { int queue_pos = ++m_max_queue_pos; torrent_ptr = boost::make_shared(boost::ref(*this) - , 16 * 1024, queue_pos, boost::cref(params), boost::cref(params.info_hash)); + , 16 * 1024, queue_pos, m_paused + , boost::cref(params), boost::cref(params.info_hash)); return torrent_ptr; } diff --git a/src/torrent.cpp b/src/torrent.cpp index 29ca94acd..1063e8ab5 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -168,13 +168,15 @@ namespace libtorrent #endif torrent_hot_members::torrent_hot_members(aux::session_interface& ses - , add_torrent_params const& p, int block_size) + , add_torrent_params const& p, int const block_size + , bool const session_paused) : m_ses(ses) , m_complete(0xffffff) , m_upload_mode((p.flags & add_torrent_params::flag_upload_mode) != 0) , m_connections_initialized(false) , m_abort(false) , m_paused((p.flags & add_torrent_params::flag_paused) != 0) + , m_session_paused(session_paused) , m_share_mode((p.flags & add_torrent_params::flag_share_mode) != 0) , m_have_all(false) , m_graceful_pause_mode(false) @@ -186,11 +188,12 @@ namespace libtorrent torrent::torrent( aux::session_interface& ses - , int block_size - , int seq + , int const block_size + , int const seq + , bool const session_paused , add_torrent_params const& p , sha1_hash const& info_hash) - : torrent_hot_members(ses, p, block_size) + : torrent_hot_members(ses, p, block_size, session_paused) , m_total_uploaded(0) , m_total_downloaded(0) , m_tracker_timer(ses.get_io_service()) @@ -322,19 +325,18 @@ namespace libtorrent bool const multi_file = m_torrent_file->is_valid() && m_torrent_file->num_files() > 1; - for (std::vector::const_iterator i = p.url_seeds.begin() - , end(p.url_seeds.end()); i != end; ++i) + for (auto const& u : p.url_seeds) { - m_web_seeds.push_back(web_seed_t(*i, web_seed_entry::url_seed)); + m_web_seeds.push_back(web_seed_t(u, web_seed_entry::url_seed)); // correct URLs to end with a "/" for multi-file torrents std::string& url = m_web_seeds.back().url; if (multi_file && url[url.size()-1] != '/') url += '/'; } - for (std::vector::const_iterator i = p.http_seeds.begin() - , end(p.http_seeds.end()); i != end; ++i) + + for (auto const& e : p.http_seeds) { - m_web_seeds.push_back(web_seed_t(*i, web_seed_entry::http_seed)); + m_web_seeds.push_back(web_seed_t(e, web_seed_entry::http_seed)); } // --- TRACKERS --- @@ -346,14 +348,12 @@ namespace libtorrent } int tier = 0; - std::vector::const_iterator tier_iter = p.tracker_tiers.begin(); - for (std::vector::const_iterator i = p.trackers.begin() - , end(p.trackers.end()); i != end; ++i) + auto tier_iter = p.tracker_tiers.begin(); + for (announce_entry e : p.trackers) { if (tier_iter != p.tracker_tiers.end()) tier = *tier_iter++; - announce_entry e(*i); e.fail_limit = 0; e.source = announce_entry::source_magnet_link; e.tier = tier; @@ -8941,7 +8941,7 @@ namespace libtorrent && !has_error() && !m_abort && !m_graceful_pause_mode - && !m_ses.is_paused(); + && !m_session_paused; } void torrent::flush_cache() @@ -8972,7 +8972,7 @@ namespace libtorrent bool torrent::is_paused() const { - return m_paused || m_ses.is_paused() || m_graceful_pause_mode; + return m_paused || m_session_paused || m_graceful_pause_mode; } void torrent::pause(bool graceful) @@ -9028,9 +9028,15 @@ namespace libtorrent if (is_finished()) m_finished_time += m_ses.session_time() - m_became_finished; + m_announce_to_dht = false; + m_announce_to_trackers = false; + m_announce_to_lsd = false; + state_updated(); update_want_peers(); update_want_scrape(); + update_gauge(); + update_state_list(); #ifndef TORRENT_DISABLE_LOGGING log_to_all_peers("pausing"); @@ -9055,7 +9061,7 @@ namespace libtorrent // files and flush all cached data if (m_storage.get() && clear_disk_cache) { - TORRENT_ASSERT(m_storage); + // the torrent_paused alert will be posted from on_torrent_paused m_ses.disk_thread().async_stop_torrent(m_storage.get() , std::bind(&torrent::on_torrent_paused, shared_from_this(), _1)); } @@ -9073,10 +9079,8 @@ namespace libtorrent // and choke all remaining peers to prevent responding to new // requests std::vector to_disconnect; - for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) + for (peer_connection* p : m_connections) { - peer_connection* p = *i; TORRENT_ASSERT(p->associated_torrent().lock().get() == this); if (p->is_disconnecting()) continue; @@ -9095,10 +9099,9 @@ namespace libtorrent to_disconnect.push_back(p); } - for (peer_iterator i = to_disconnect.begin(); i != to_disconnect.end(); ++i) - { - peer_connection* p = *i; + for (peer_connection* p : to_disconnect) + { // since we're currently in graceful pause mode, the last peer to // disconnect (assuming all peers end up begin disconnected here) // will post the torrent_paused_alert @@ -9146,6 +9149,18 @@ namespace libtorrent set_need_save_resume(); } + void torrent::set_session_paused(bool const b) + { + if (m_session_paused == b) return; + bool const paused_before = is_paused(); + m_session_paused = b; + + if (paused_before == is_paused()) return; + + if (b) do_pause(); + else do_resume(); + } + void torrent::set_paused(bool b, int flags) { TORRENT_ASSERT(is_single_thread()); @@ -9174,32 +9189,16 @@ namespace libtorrent return; } + bool const paused_before = is_paused(); + m_paused = b; - if (!m_ses.is_paused()) - m_graceful_pause_mode = (flags & flag_graceful_pause) ? true : false; - if (b) - { - m_announce_to_dht = false; - m_announce_to_trackers = false; - m_announce_to_lsd = false; - } + if (paused_before == is_paused()) return; - update_gauge(); - update_want_scrape(); - update_want_peers(); - update_state_list(); - state_updated(); - - if (b) - { - do_pause((flags & flag_clear_disk_cache) != 0); - } - else - { - do_resume(); - } + m_graceful_pause_mode = (flags & flag_graceful_pause) ? true : false; + if (b) do_pause((flags & flag_clear_disk_cache) != 0); + else do_resume(); } void torrent::resume() @@ -9216,7 +9215,7 @@ namespace libtorrent m_announce_to_trackers = true; m_announce_to_lsd = true; m_paused = false; - if (!m_ses.is_paused()) m_graceful_pause_mode = false; + if (!m_session_paused) m_graceful_pause_mode = false; update_gauge(); @@ -9265,6 +9264,9 @@ namespace libtorrent update_want_peers(); update_want_tick(); update_want_scrape(); + update_gauge(); + + if (should_check_files()) start_checking(); if (m_state == torrent_status::checking_files) return; @@ -9837,8 +9839,8 @@ namespace libtorrent return; // now, pick one of the rarest pieces to download - int pick = random() % rarest_pieces.size(); - bool was_finished = is_finished(); + int const pick = random() % rarest_pieces.size(); + bool const was_finished = is_finished(); m_picker->set_piece_priority(rarest_pieces[pick], 1); update_gauge(); update_peer_interest(was_finished);