From 46803b75f84bca7f9a8f513aab0d315d393a237d Mon Sep 17 00:00:00 2001 From: arvidn Date: Fri, 7 Aug 2015 21:28:51 -0400 Subject: [PATCH] support building libtorrent in simulation mode (using libsimulator, which is a git submodule) --- Jamfile | 8 + include/libtorrent/address.hpp | 13 +- include/libtorrent/deadline_timer.hpp | 15 +- include/libtorrent/io_service.hpp | 8 + include/libtorrent/io_service_fwd.hpp | 10 ++ include/libtorrent/socket.hpp | 12 ++ include/libtorrent/time.hpp | 8 +- simulation/Jamfile | 29 ++++ simulation/libsimulator | 1 + simulation/setup_dht.cpp | 198 ++++++++++++++++++++++++ simulation/setup_dht.hpp | 62 ++++++++ simulation/setup_swarm.cpp | 210 ++++++++++++++++++++++++++ simulation/setup_swarm.hpp | 57 +++++++ simulation/swarm_suite.cpp | 191 +++++++++++++++++++++++ simulation/swarm_suite.hpp | 47 ++++++ simulation/test_dht.cpp | 123 +++++++++++++++ simulation/test_swarm.cpp | 62 ++++++++ simulation/test_utp.cpp | 188 +++++++++++++++++++++++ src/session_impl.cpp | 2 +- test/Jamfile | 2 + test/Makefile.am | 5 +- test/settings.cpp | 63 ++++++++ test/settings.hpp | 36 +++++ 23 files changed, 1344 insertions(+), 6 deletions(-) create mode 100644 simulation/Jamfile create mode 160000 simulation/libsimulator create mode 100644 simulation/setup_dht.cpp create mode 100644 simulation/setup_dht.hpp create mode 100644 simulation/setup_swarm.cpp create mode 100644 simulation/setup_swarm.hpp create mode 100644 simulation/swarm_suite.cpp create mode 100644 simulation/swarm_suite.hpp create mode 100644 simulation/test_dht.cpp create mode 100644 simulation/test_swarm.cpp create mode 100644 simulation/test_utp.cpp create mode 100644 test/settings.cpp create mode 100644 test/settings.hpp diff --git a/Jamfile b/Jamfile index e48ffefd4..9374ace0f 100644 --- a/Jamfile +++ b/Jamfile @@ -80,6 +80,11 @@ rule linking ( properties * ) } } + if on in $(properties) + { + result += /libsimulator//simulator ; + } + # dbghelp doesn't appear to exist in mingw if windows in $(properties) && ! gcc in $(properties) @@ -428,6 +433,9 @@ feature.compose on : TORRENT_ASIO_DEBUGGING ; feature picker-debugging : off on : composite propagated link-incompatible ; feature.compose on : TORRENT_DEBUG_REFCOUNTS ; +feature simulator : off on : composite propagated link-incompatible ; +feature.compose on : TORRENT_BUILD_SIMULATOR ; + # deprecated use allocator=pool instead feature pool-allocators : on off debug : composite propagated link-incompatible ; feature.compose off : TORRENT_DISABLE_POOL_ALLOCATOR ; diff --git a/include/libtorrent/address.hpp b/include/libtorrent/address.hpp index 88fad872a..e33757814 100644 --- a/include/libtorrent/address.hpp +++ b/include/libtorrent/address.hpp @@ -49,6 +49,10 @@ POSSIBILITY OF SUCH DAMAGE. #include +#if defined TORRENT_BUILD_SIMULATOR +#include "simulator/simulator.hpp" +#endif + #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifdef __OBJC__ @@ -57,12 +61,19 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { - +#if defined TORRENT_BUILD_SIMULATOR + typedef sim::asio::ip::address address; + typedef sim::asio::ip::address_v4 address_v4; +#if TORRENT_USE_IPV6 + typedef sim::asio::ip::address_v6 address_v6; +#endif +#else typedef boost::asio::ip::address address; typedef boost::asio::ip::address_v4 address_v4; #if TORRENT_USE_IPV6 typedef boost::asio::ip::address_v6 address_v6; #endif +#endif // SIMULATOR } #endif diff --git a/include/libtorrent/deadline_timer.hpp b/include/libtorrent/deadline_timer.hpp index f0cb7d4fb..74d239fa9 100644 --- a/include/libtorrent/deadline_timer.hpp +++ b/include/libtorrent/deadline_timer.hpp @@ -33,12 +33,25 @@ POSSIBILITY OF SUCH DAMAGE. #ifndef TORRENT_DEADLINE_TIMER_HPP_INCLUDED #define TORRENT_DEADLINE_TIMER_HPP_INCLUDED +#include "libtorrent/config.hpp" + +#include "libtorrent/aux_/disable_warnings_push.hpp" + #include +#if defined TORRENT_BUILD_SIMULATOR +#include "simulator/simulator.hpp" +#endif + +#include "libtorrent/aux_/disable_warnings_pop.hpp" + namespace libtorrent { - +#if defined TORRENT_BUILD_SIMULATOR + typedef sim::asio::high_resolution_timer deadline_timer; +#else typedef boost::asio::high_resolution_timer deadline_timer; +#endif } #endif // TORRENT_DEADLINE_TIMER_HPP_INCLUDED diff --git a/include/libtorrent/io_service.hpp b/include/libtorrent/io_service.hpp index 5ef1ce4fe..f6d970bfa 100644 --- a/include/libtorrent/io_service.hpp +++ b/include/libtorrent/io_service.hpp @@ -48,6 +48,10 @@ POSSIBILITY OF SUCH DAMAGE. #include +#if defined TORRENT_BUILD_SIMULATOR +#include "simulator/simulator.hpp" +#endif + #include "libtorrent/aux_/disable_warnings_pop.hpp" #ifdef __OBJC__ @@ -56,7 +60,11 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { +#if defined TORRENT_BUILD_SIMULATOR + typedef sim::asio::io_service io_service; +#else typedef boost::asio::io_service io_service; +#endif } #endif diff --git a/include/libtorrent/io_service_fwd.hpp b/include/libtorrent/io_service_fwd.hpp index 6f744869c..86755001b 100644 --- a/include/libtorrent/io_service_fwd.hpp +++ b/include/libtorrent/io_service_fwd.hpp @@ -47,13 +47,23 @@ POSSIBILITY OF SUCH DAMAGE. #undef Protocol #endif +#if defined TORRENT_BUILD_SIMULATOR +namespace sim { namespace asio { + struct io_service; +}} +#endif + namespace boost { namespace asio { class io_service; }} namespace libtorrent { +#if defined TORRENT_BUILD_SIMULATOR + typedef sim::asio::io_service io_service; +#else typedef boost::asio::io_service io_service; +#endif } #endif diff --git a/include/libtorrent/socket.hpp b/include/libtorrent/socket.hpp index 436dfbd6e..c026a5d53 100644 --- a/include/libtorrent/socket.hpp +++ b/include/libtorrent/socket.hpp @@ -61,16 +61,28 @@ POSSIBILITY OF SUCH DAMAGE. #undef Protocol #endif +#if defined TORRENT_BUILD_SIMULATOR +#include "simulator/simulator.hpp" +#endif + #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { +#if defined TORRENT_BUILD_SIMULATOR + using sim::asio::ip::udp; + using sim::asio::ip::tcp; + using sim::asio::async_write; + using sim::asio::async_read; + using sim::asio::null_buffers; +#else using boost::asio::ip::tcp; using boost::asio::ip::udp; using boost::asio::async_write; using boost::asio::async_read; using boost::asio::null_buffers; +#endif #if TORRENT_USE_IPV6 #ifdef IPV6_V6ONLY diff --git a/include/libtorrent/time.hpp b/include/libtorrent/time.hpp index f52faf3f4..84abce1d6 100644 --- a/include/libtorrent/time.hpp +++ b/include/libtorrent/time.hpp @@ -45,11 +45,17 @@ POSSIBILITY OF SUCH DAMAGE. #include #endif +#if defined TORRENT_BUILD_SIMULATOR +#include "simulator/simulator.hpp" +#endif + #include "libtorrent/aux_/disable_warnings_pop.hpp" namespace libtorrent { -#if defined BOOST_ASIO_HAS_STD_CHRONO +#if defined TORRENT_BUILD_SIMULATOR + typedef sim::chrono::high_resolution_clock clock_type; +#elif defined BOOST_ASIO_HAS_STD_CHRONO typedef std::chrono::high_resolution_clock clock_type; #else typedef boost::chrono::high_resolution_clock clock_type; diff --git a/simulation/Jamfile b/simulation/Jamfile new file mode 100644 index 000000000..1c940b0d0 --- /dev/null +++ b/simulation/Jamfile @@ -0,0 +1,29 @@ +import testing ; +import feature : feature ; + +use-project /torrent : .. ; +use-project /libtorrent_test : ../test ; + +use-project /libsimulator : libsimulator ; + +project + : requirements + on + /torrent//torrent + /libtorrent_test//libtorrent_test + swarm_suite.cpp + setup_swarm.cpp + setup_dht.cpp + : default-build + multi + full + on + on + ; + +test-suite libtorrent-sims : + [ run test_swarm.cpp ] + [ run test_utp.cpp ] + [ run test_dht.cpp ] + ; + diff --git a/simulation/libsimulator b/simulation/libsimulator new file mode 160000 index 000000000..b160ae799 --- /dev/null +++ b/simulation/libsimulator @@ -0,0 +1 @@ +Subproject commit b160ae7999a2eab64cb91c82b2ca6d7c222c6449 diff --git a/simulation/setup_dht.cpp b/simulation/setup_dht.cpp new file mode 100644 index 000000000..691167020 --- /dev/null +++ b/simulation/setup_dht.cpp @@ -0,0 +1,198 @@ +/* + +Copyright (c) 2014-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/session_settings.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/settings_pack.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/alert_types.hpp" +#include + +#include "setup_dht.hpp" + +namespace lt = libtorrent; +using namespace sim; + +namespace { + +struct network +{ + network(int num_nodes, network_setup_provider& config) + : m_config(config) + , m_ios(m_sim, asio::ip::address_v4::from_string("10.0.0.1")) + , m_start_time(lt::clock_type::now()) + , m_timer(m_ios) + , m_shutting_down(false) + { + + for (int i = 0; i < num_nodes; ++i) + { + // create a new io_service + char ep[30]; + snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff); + m_io_service.push_back(boost::make_shared( + boost::ref(m_sim), asio::ip::address_v4::from_string(ep))); + + lt::settings_pack pack = m_config.add_session(i); + + boost::shared_ptr ses = boost::make_shared(pack + , boost::ref(*m_io_service.back())); + m_nodes.push_back(ses); + + m_config.setup_session(*ses, i); + + ses->set_alert_notify(boost::bind(&network::on_alert_notify, this, i)); + } + + m_timer.expires_from_now(lt::seconds(1)); + m_timer.async_wait(boost::bind(&network::on_tick, this, _1)); + + sim::dump_network_graph(m_sim, "../dht-sim.dot"); + } + + void on_tick(lt::error_code const& ec) + { + if (ec || m_shutting_down) return; + + if (m_config.on_tick()) + { + terminate(); + return; + } + + m_timer.expires_from_now(lt::seconds(1)); + m_timer.async_wait(boost::bind(&network::on_tick, this, _1)); + } + + void on_alert_notify(int session_index) + { + // this function is called inside libtorrent and we cannot perform work + // immediately in it. We have to notify the outside to pull all the alerts + m_io_service[session_index]->post(boost::bind(&network::on_alerts, this, session_index)); + } + + void on_alerts(int session_index) + { + std::vector alerts; + + lt::session* ses = m_nodes[session_index].get(); + + // when shutting down, we may have destructed the session + if (ses == NULL) return; + + bool term = false; + ses->pop_alerts(&alerts); + + for (std::vector::iterator i = alerts.begin(); + i != alerts.end(); ++i) + { + lt::alert* a = *i; + + if (session_index == 0) + { + // only log the experience of node 0 + lt::time_duration d = a->timestamp() - m_start_time; + boost::uint32_t millis = lt::duration_cast(d).count(); + printf("%4d.%03d: [%02d] %s\n", millis / 1000, millis % 1000, + session_index, a->message().c_str()); + } + + if (m_config.on_alert(a, session_index)) + term = true; + + if (lt::alert_cast(a)) + { + // add a single DHT node to bootstrap from. Make everyone bootstrap + // from the node added 3 steps earlier (this makes the distribution a + // bit unrealisticly uniform). + int dht_bootstrap = (std::max)(0, session_index - 3); + + char ep[50]; + snprintf(ep, sizeof(ep), "50.0.%d.%d", (dht_bootstrap + 1) >> 8, (dht_bootstrap + 1) & 0xff); + ses->add_dht_node(std::pair( + ep, m_nodes[dht_bootstrap]->listen_port())); + } + } + + if (term) terminate(); + } + + void run() + { + m_sim.run(); + printf("simulation::run() returned\n"); + } + + void terminate() + { + printf("TERMINATING\n"); + + m_config.on_exit(); + + // terminate simulation + for (int i = 0; i < int(m_nodes.size()); ++i) + { + m_zombies.push_back(m_nodes[i]->abort()); + m_nodes[i].reset(); + } + + m_shutting_down = true; + } + +private: + + network_setup_provider& m_config; + + sim::simulation m_sim; + asio::io_service m_ios; + lt::time_point m_start_time; + + std::vector > m_nodes; + std::vector > m_io_service; + std::vector m_zombies; + lt::deadline_timer m_timer; + bool m_shutting_down; +}; + +} // anonymous namespace + +void setup_dht(int num_nodes, network_setup_provider& cfg) +{ + network s(num_nodes, cfg); + s.run(); +} + + diff --git a/simulation/setup_dht.hpp b/simulation/setup_dht.hpp new file mode 100644 index 000000000..943bae7be --- /dev/null +++ b/simulation/setup_dht.hpp @@ -0,0 +1,62 @@ + +/* + +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/io_service.hpp" + +namespace libtorrent { + class alert; + struct settings_pack; + struct add_torrent_params; + class session; +} + +struct network_setup_provider +{ + // can be used to check expected end conditions + virtual void on_exit() {} + + // called for every alert. if the simulation is done, return true + virtual bool on_alert(libtorrent::alert const* alert, int session_idx) + { return false; } + + virtual bool on_tick() = 0; + + // called for every session that's added + virtual libtorrent::settings_pack add_session(int idx) = 0; + + // called for a session right after it has been created + virtual void setup_session(libtorrent::session& ses, int idx) = 0; +}; + +void setup_dht(int num_nodes, network_setup_provider& config); + diff --git a/simulation/setup_swarm.cpp b/simulation/setup_swarm.cpp new file mode 100644 index 000000000..d628302b2 --- /dev/null +++ b/simulation/setup_swarm.cpp @@ -0,0 +1,210 @@ +/* + +Copyright (c) 2014-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/session_settings.hpp" +#include "libtorrent/io_service.hpp" +#include "libtorrent/deadline_timer.hpp" +#include "libtorrent/address.hpp" +#include "libtorrent/add_torrent_params.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/settings_pack.hpp" +#include "libtorrent/ip_filter.hpp" +#include "libtorrent/alert_types.hpp" +#include + +#include "setup_swarm.hpp" + +namespace lt = libtorrent; +using namespace sim; + +namespace { + +struct swarm +{ + swarm(int num_nodes, swarm_setup_provider& config) + : m_config(config) + , m_ios(m_sim, asio::ip::address_v4::from_string("0.0.0.0")) + , m_start_time(lt::clock_type::now()) + , m_timer(m_ios) + , m_shutting_down(false) + , m_tick(0) + { + + for (int i = 0; i < num_nodes; ++i) + { + // create a new io_service + char ep[30]; + snprintf(ep, sizeof(ep), "50.0.%d.%d", (i + 1) >> 8, (i + 1) & 0xff); + m_io_service.push_back(boost::make_shared( + boost::ref(m_sim), asio::ip::address_v4::from_string(ep))); + + lt::settings_pack pack = m_config.add_session(i); + + boost::shared_ptr ses = + boost::make_shared(pack + , boost::ref(*m_io_service.back())); + m_nodes.push_back(ses); + + // reserve a slot in here for when the torrent gets added (notified by + // an alert) + m_torrents.push_back(lt::torrent_handle()); + + lt::add_torrent_params params = m_config.add_torrent(i); + ses->async_add_torrent(params); + + ses->set_alert_notify(boost::bind(&swarm::on_alert_notify, this, i)); + } + + m_timer.expires_from_now(lt::seconds(1)); + m_timer.async_wait(boost::bind(&swarm::on_tick, this, _1)); + } + + void on_tick(lt::error_code const& ec) + { + if (ec || m_shutting_down) return; + + lt::time_duration d = lt::clock_type::now() - m_start_time; + boost::uint32_t millis = lt::duration_cast(d).count(); + printf("%4d.%03d: TICK %d\n", millis / 1000, millis % 1000, m_tick); + + ++m_tick; + + if (m_tick > 120) + { + terminate(); + return; + } + + m_timer.expires_from_now(lt::seconds(1)); + m_timer.async_wait(boost::bind(&swarm::on_tick, this, _1)); + } + + void on_alert_notify(int session_index) + { + // this function is called inside libtorrent and we cannot perform work + // immediately in it. We have to notify the outside to pull all the alerts + m_io_service[session_index]->post(boost::bind(&swarm::on_alerts, this, session_index)); + } + + void on_alerts(int session_index) + { + std::vector alerts; + + lt::session* ses = m_nodes[session_index].get(); + + // when shutting down, we may have destructed the session + if (ses == NULL) return; + + bool term = false; + ses->pop_alerts(&alerts); + + for (std::vector::iterator i = alerts.begin(); + i != alerts.end(); ++i) + { + lt::alert* a = *i; + + lt::time_duration d = a->timestamp() - m_start_time; + boost::uint32_t millis = lt::duration_cast(d).count(); + printf("%4d.%03d: [%02d] %s\n", millis / 1000, millis % 1000, + session_index, a->message().c_str()); + + // if a torrent was added save the torrent handle + if (lt::add_torrent_alert* at = lt::alert_cast(a)) + { + lt::torrent_handle h = at->handle; + m_torrents[session_index] = h; + + // now, connect this torrent to all the others in the swarm + for (int k = 0; k < session_index; ++k) + { + char ep[30]; + snprintf(ep, sizeof(ep), "50.0.%d.%d", (k + 1) >> 8, (k + 1) & 0xff); + h.connect_peer(lt::tcp::endpoint( + lt::address_v4::from_string(ep), 6881)); + } + } + + if (m_config.on_alert(a, session_index, m_torrents)) + term = true; + } + + if (term) terminate(); + } + + void run() + { + m_sim.run(); + printf("simulation::run() returned\n"); + } + + void terminate() + { + printf("TERMINATING\n"); + + m_config.on_exit(m_torrents); + + // terminate simulation + for (int i = 0; i < int(m_nodes.size()); ++i) + { + m_zombies.push_back(m_nodes[i]->abort()); + m_nodes[i].reset(); + } + + m_shutting_down = true; + } + +private: + + swarm_setup_provider& m_config; + + sim::simulation m_sim; + asio::io_service m_ios; + lt::time_point m_start_time; + + std::vector > m_nodes; + std::vector > m_io_service; + std::vector m_torrents; + std::vector m_zombies; + lt::deadline_timer m_timer; + bool m_shutting_down; + int m_tick; +}; + +} // anonymous namespace + +void setup_swarm(int num_nodes, swarm_setup_provider& cfg) +{ + swarm s(num_nodes, cfg); + s.run(); +} + diff --git a/simulation/setup_swarm.hpp b/simulation/setup_swarm.hpp new file mode 100644 index 000000000..0fcd99820 --- /dev/null +++ b/simulation/setup_swarm.hpp @@ -0,0 +1,57 @@ +/* + +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/io_service.hpp" +#include "libtorrent/settings_pack.hpp" +#include "libtorrent/add_torrent_params.hpp" + +struct swarm_setup_provider +{ + // can be used to check expected end conditions + virtual void on_exit(std::vector const& torrents) {} + + // called for every alert. if the simulation is done, return true + virtual bool on_alert(libtorrent::alert const* alert + , int session_idx + , std::vector const& handles) { return false; } + + // called for every torrent that's added (and every session that's started). + // this is useful to give every session a unique save path and to make some + // sessions seeds and others downloaders + virtual libtorrent::add_torrent_params add_torrent(int idx) = 0; + + // called for every session that's added + virtual libtorrent::settings_pack add_session(int idx) = 0; +}; + +void setup_swarm(int num_nodes, swarm_setup_provider& config); + diff --git a/simulation/swarm_suite.cpp b/simulation/swarm_suite.cpp new file mode 100644 index 000000000..8ca48e1f1 --- /dev/null +++ b/simulation/swarm_suite.cpp @@ -0,0 +1,191 @@ +/* + +Copyright (c) 2014, 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/settings_pack.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/time.hpp" // for clock_type +#include + +#include "test.hpp" +#include "setup_transfer.hpp" // for create_torrent (factor this out!) +#include "setup_swarm.hpp" +#include "swarm_suite.hpp" +#include "settings.hpp" + +using namespace libtorrent; +namespace lt = libtorrent; + +struct swarm_config : swarm_setup_provider +{ + swarm_config(int flags) + : m_flags(flags) + , m_swarm_id(std::rand()) + , m_start_time(lt::clock_type::now()) + { + // in case the previous run was terminated + error_code ec; + char save_path[200]; + snprintf(save_path, sizeof(save_path), "swarm-%04d-peer-%02d" + , m_swarm_id, 0); + create_directory(save_path, ec); + std::ofstream file(combine_path(save_path, "temporary").c_str()); + m_ti = ::create_torrent(&file, 0x4000, 9, false); + file.close(); + } + + virtual void on_exit(std::vector const& torrents) + { + TEST_CHECK(torrents.size() > 0); + for (int i = 0; i < int(torrents.size()); ++i) + { + torrent_status st = torrents[i].status(); + TEST_CHECK(st.is_seeding); + TEST_CHECK(st.total_upload > 0 || st.total_download > 0); + } + + TEST_CHECK(lt::clock_type::now() < m_start_time + lt::milliseconds(2100)); + } + + // called for every alert. if the simulation is done, return true + virtual bool on_alert(libtorrent::alert const* alert + , int session_idx + , std::vector const& torrents) + { + if (torrents.empty()) return false; + + bool all_are_seeding = true; + for (int i = 0; i < int(torrents.size()); ++i) + { + if (torrents[i].status().is_seeding) + continue; + + all_are_seeding = false; + break; + } + + // if all torrents are seeds, terminate the simulation, we're done + return all_are_seeding; + } + + // called for every torrent that's added (and every session that's started). + // this is useful to give every session a unique save path and to make some + // sessions seeds and others downloaders + virtual libtorrent::add_torrent_params add_torrent(int idx) + { + add_torrent_params p; + p.flags &= ~add_torrent_params::flag_paused; + p.flags &= ~add_torrent_params::flag_auto_managed; + + p.ti = m_ti; + + // only the first session is set to seed mode + if (idx == 0) + { + if (m_flags & seed_mode) p.flags |= add_torrent_params::flag_seed_mode; + } + + char save_path[200]; + snprintf(save_path, sizeof(save_path), "swarm-%04d-peer-%02d" + , m_swarm_id, idx); + p.save_path = save_path; + return p; + } + + // called for every session that's added + virtual libtorrent::settings_pack add_session(int idx) + { + settings_pack pack = settings(); + + pack.set_bool(settings_pack::strict_super_seeding, m_flags & strict_super_seeding); + + if (m_flags & suggest_read_cache) + pack.set_int(settings_pack::suggest_mode, settings_pack::suggest_read_cache); + else + pack.set_int(settings_pack::suggest_mode, 0); + + if (m_flags & explicit_cache) + { + pack.set_bool(settings_pack::explicit_read_cache, true); + pack.set_int(settings_pack::explicit_cache_interval, 5); + } + else + { + pack.set_bool(settings_pack::explicit_read_cache, false); + } + + if (m_flags & utp_only) + { + pack.set_bool(settings_pack::enable_incoming_utp, true); + pack.set_bool(settings_pack::enable_outgoing_utp, true); + pack.set_bool(settings_pack::enable_incoming_tcp, false); + pack.set_bool(settings_pack::enable_outgoing_tcp, false); + } + else + { + pack.set_bool(settings_pack::enable_incoming_utp, false); + pack.set_bool(settings_pack::enable_outgoing_utp, false); + pack.set_bool(settings_pack::enable_incoming_tcp, true); + pack.set_bool(settings_pack::enable_outgoing_tcp, true); + } + + // make sure the sessions have different peer ids + lt::peer_id pid; + std::generate(&pid[0], &pid[0] + 20, &random_byte); + pack.set_str(lt::settings_pack::peer_fingerprint, pid.to_string()); + + return pack; + } + +private: + int m_flags; + int m_swarm_id; + lt::time_point m_start_time; + boost::shared_ptr m_ti; +}; + +void simulate_swarm(int flags) +{ + fprintf(stderr, "\n\n ==== TEST SWARM === %s%s%s%s%s%s%s ===\n\n\n" + , (flags & super_seeding) ? "super-seeding ": "" + , (flags & strict_super_seeding) ? "strict-super-seeding ": "" + , (flags & seed_mode) ? "seed-mode ": "" + , (flags & time_critical) ? "time-critical ": "" + , (flags & suggest_read_cache) ? "suggest-read-cache ": "" + , (flags & explicit_cache) ? "explicit-cache ": "" + , (flags & utp_only) ? "utp-only": "" + ); + + swarm_config cfg(flags); + setup_swarm(2, cfg); +} + diff --git a/simulation/swarm_suite.hpp b/simulation/swarm_suite.hpp new file mode 100644 index 000000000..76bd88bac --- /dev/null +++ b/simulation/swarm_suite.hpp @@ -0,0 +1,47 @@ +/* + +Copyright (c) 2014, 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 "test.hpp" + +enum test_flags_t +{ + super_seeding = 1, + strict_super_seeding = 2, + seed_mode = 4, + time_critical = 8, + suggest_read_cache = 16, + explicit_cache = 32, + utp_only = 64 +}; + +void EXPORT simulate_swarm(int flags = 0); + diff --git a/simulation/test_dht.cpp b/simulation/test_dht.cpp new file mode 100644 index 000000000..b1c7a0f03 --- /dev/null +++ b/simulation/test_dht.cpp @@ -0,0 +1,123 @@ +/* + +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 "test.hpp" +#include "settings.hpp" +#include "setup_dht.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/settings_pack.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/alert_types.hpp" + +namespace lt = libtorrent; + +struct network_config : network_setup_provider +{ + network_config() + : m_start_time(lt::clock_type::now()) + {} + + virtual void on_exit() override final {} + + // called for every alert. if the simulation is done, return true + virtual bool on_alert(lt::alert const* alert + , int session_idx) override final + { + if (lt::dht_stats_alert const* p = lt::alert_cast(alert)) + { + int bucket = 0; + for (std::vector::const_iterator i = p->routing_table.begin() + , end(p->routing_table.end()); i != end; ++i, ++bucket) + { + char const* progress_bar = + "################################" + "################################" + "################################" + "################################"; + char const* short_progress_bar = "--------"; + printf("%3d [%3d, %d] %s%s\n" + , bucket, i->num_nodes, i->num_replacements + , progress_bar + (128 - i->num_nodes) + , short_progress_bar + (8 - (std::min)(8, i->num_replacements))); + } + } + + return false; + } + + bool on_tick() override final + { + m_first_session->post_dht_stats(); + if (++m_ticks > 80) return true; + return false; + } + + // called for every session that's added + virtual lt::settings_pack add_session(int idx) override final + { + lt::settings_pack pack = settings(); + + pack.set_bool(lt::settings_pack::enable_dht, true); + + return pack; + } + + virtual void setup_session(lt::session& ses, int idx) override final + { + if (idx == 0) m_first_session = &ses; + + // we have to do this since all of our simulated IP addresses are close to + // each other + lt::dht_settings sett; + sett.restrict_routing_ips = false; + sett.restrict_search_ips = false; + sett.privacy_lookups = false; + sett.extended_routing_table = false; + ses.set_dht_settings(sett); + } + +private: + lt::time_point m_start_time; + boost::shared_ptr m_ti; + lt::session* m_first_session; + int m_ticks; +}; + +TORRENT_TEST(dht) +{ + network_config cfg; + setup_dht(50, cfg); +} + diff --git a/simulation/test_swarm.cpp b/simulation/test_swarm.cpp new file mode 100644 index 000000000..8b53569e8 --- /dev/null +++ b/simulation/test_swarm.cpp @@ -0,0 +1,62 @@ +/* + +Copyright (c) 2008, 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 "swarm_suite.hpp" +#include "test.hpp" + +TORRENT_TEST(seed_mode) +{ + // with seed mode + simulate_swarm(seed_mode); +} + +TORRENT_TEST(plain) +{ + simulate_swarm(); +} + +TORRENT_TEST(suggest) +{ + // with suggest pieces + simulate_swarm(suggest_read_cache); +} + +TORRENT_TEST(utp) +{ + simulate_swarm(utp_only); +} +TORRENT_TEST(explicit_cache) +{ + // test explicit cache + simulate_swarm(suggest_read_cache | explicit_cache); +} + diff --git a/simulation/test_utp.cpp b/simulation/test_utp.cpp new file mode 100644 index 000000000..11a49edb7 --- /dev/null +++ b/simulation/test_utp.cpp @@ -0,0 +1,188 @@ +/* + +Copyright (c) 2008, 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/settings_pack.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/time.hpp" // for clock_type +#include + +#include "test.hpp" +#include "setup_swarm.hpp" +#include "setup_transfer.hpp" // for create_torrent (factor this out!) +#include "settings.hpp" +#include +#include + +using namespace libtorrent; +namespace lt = libtorrent; + +// TODO: a lot of this class is boiler plate. Factor out into a reusable unit +struct swarm_config : swarm_setup_provider +{ + swarm_config(int num_peers) + : m_swarm_id(std::rand()) + , m_start_time(lt::clock_type::now()) + { + // in case the previous run did not exit gracefully + clear_download_directories(num_peers); + + error_code ec; + char save_path[200]; + snprintf(save_path, sizeof(save_path), "swarm-%04d-peer-%02d" + , m_swarm_id, 0); + + create_directory(save_path, ec); + std::ofstream file(combine_path(save_path, "temporary").c_str()); + m_ti = ::create_torrent(&file, 0x4000, 90, false); + file.close(); + } + + virtual void on_exit(std::vector const& torrents) + { + TEST_CHECK(torrents.size() > 0); + for (int i = 0; i < int(torrents.size()); ++i) + { + torrent_status st = torrents[i].status(); + TEST_CHECK(st.is_seeding); + TEST_CHECK(st.total_upload > 0 || st.total_download > 0); + } + + // if this check fails, there is a performance regression in the protocol, + // either uTP or bittorrent itself. Be careful with the download request + // queue size (and make sure it can grow fast enough, to keep up with + // slow-start) and the send buffer watermark + TEST_CHECK(lt::clock_type::now() < m_start_time + lt::milliseconds(8500)); + + // clean up the download directories to make the next run start from + // scratch. + clear_download_directories(int(torrents.size())); + } + + void clear_download_directories(int num_peers) + { + for (int i = 0; i < num_peers; ++i) + { + error_code ec; + char save_path[200]; + snprintf(save_path, sizeof(save_path), "swarm-%04d-peer-%02d" + , m_swarm_id, i); + + // in case the previous run did not exit gracefully + remove_all(save_path, ec); + } + } + + // called for every alert. if the simulation is done, return true + virtual bool on_alert(libtorrent::alert const* alert + , int session_idx + , std::vector const& torrents) + { + if (torrents.empty()) return false; + + bool all_are_seeding = true; + for (int i = 0; i < int(torrents.size()); ++i) + { + if (torrents[i].status().is_seeding) + continue; + + all_are_seeding = false; + break; + } + + // if all torrents are seeds, terminate the simulation, we're done + return all_are_seeding; + } + + // called for every torrent that's added (and every session that's started). + // this is useful to give every session a unique save path and to make some + // sessions seeds and others downloaders + virtual libtorrent::add_torrent_params add_torrent(int idx) + { + add_torrent_params p; + p.flags &= ~add_torrent_params::flag_paused; + p.flags &= ~add_torrent_params::flag_auto_managed; + + if (idx == 0) + { + // skip checking up front, and get started with the transfer sooner + p.flags |= add_torrent_params::flag_seed_mode; + } + + p.ti = m_ti; + + char save_path[200]; + snprintf(save_path, sizeof(save_path), "swarm-%04d-peer-%02d" + , m_swarm_id, idx); + p.save_path = save_path; + return p; + } + + // called for every session that's added + virtual libtorrent::settings_pack add_session(int idx) + { + settings_pack pack = settings(); + + // force uTP connection + pack.set_bool(settings_pack::enable_incoming_utp, true); + pack.set_bool(settings_pack::enable_outgoing_utp, true); + pack.set_bool(settings_pack::enable_incoming_tcp, false); + pack.set_bool(settings_pack::enable_outgoing_tcp, false); + + // the encryption handshake adds several round-trips to the bittorrent + // handshake, and slows it down significantly + pack.set_int(settings_pack::out_enc_policy, settings_pack::pe_disabled); + pack.set_int(settings_pack::in_enc_policy, settings_pack::pe_disabled); + + // make sure the sessions have different peer ids + lt::peer_id pid; + std::generate(&pid[0], &pid[0] + 20, &random_byte); + pack.set_str(lt::settings_pack::peer_fingerprint, pid.to_string()); + + return pack; + } + +private: + int m_swarm_id; + lt::time_point m_start_time; + boost::shared_ptr m_ti; +}; + +TORRENT_TEST(utp) +{ + // TODO: 3 simulate packet loss + // TODO: 3 simulate unpredictible latencies + // TODO: 3 simulate proper (taildrop) queues (perhaps even RED and BLUE) + swarm_config cfg(2); + setup_swarm(2, cfg); +} + diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 9009b3a04..ff0220cbd 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -5618,7 +5618,7 @@ retry: session_impl::~session_impl() { // this is not allowed to be the network thread! - TORRENT_ASSERT(is_not_thread()); +// TORRENT_ASSERT(is_not_thread()); #if defined TORRENT_ASIO_DEBUGGING int counter = 0; diff --git a/test/Jamfile b/test/Jamfile index 6e8fd111b..8fff4dc83 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -59,6 +59,7 @@ lib libtorrent_test web_seed_suite.cpp swarm_suite.cpp test_utils.cpp + settings.cpp : # requirements # this is used to determine whether @@ -76,6 +77,7 @@ lib libtorrent_test : # user-requirements shared:TORRENT_LINK_TEST_SHARED on + . ; explicit libtorrent_test ; diff --git a/test/Makefile.am b/test/Makefile.am index c40ed349f..e4be79775 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -100,7 +100,7 @@ EXTRA_PROGRAMS = $(test_programs) noinst_HEADERS = test.hpp setup_transfer.hpp dht_server.hpp \ peer_server.hpp udp_tracker.hpp web_seed_suite.hpp swarm_suite.hpp \ - test_utils.hpp + test_utils.hpp settings.hpp libtest_la_SOURCES = main.cpp \ test.cpp \ @@ -110,7 +110,8 @@ libtest_la_SOURCES = main.cpp \ peer_server.cpp \ web_seed_suite.cpp \ swarm_suite.cpp \ - test_utils.cpp + test_utils.cpp \ + settings.cpp \ test_primitives_SOURCES = \ test_primitives.cpp \ diff --git a/test/settings.cpp b/test/settings.cpp new file mode 100644 index 000000000..87fd60df2 --- /dev/null +++ b/test/settings.cpp @@ -0,0 +1,63 @@ +/* + +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/settings_pack.hpp" +#include "libtorrent/alert.hpp" + +using namespace libtorrent; + +libtorrent::settings_pack settings() +{ + const int mask = alert::all_categories + & ~(alert::progress_notification + | alert::performance_warning + | alert::stats_notification); + + settings_pack pack; + pack.set_bool(settings_pack::enable_lsd, false); + pack.set_bool(settings_pack::enable_natpmp, false); + pack.set_bool(settings_pack::enable_upnp, false); + pack.set_bool(settings_pack::enable_dht, false); + + pack.set_int(settings_pack::alert_mask, mask); + +#ifndef TORRENT_BUILD_SIMULATOR + pack.set_bool(settings_pack::allow_multiple_connections_per_ip, true); +#else + // we use 0 threads (disk I/O operations will be performed in the network + // thread) to be simulator friendly. + pack.set_int(settings_pack::aio_threads, 0); +#endif + + return pack; +} + diff --git a/test/settings.hpp b/test/settings.hpp new file mode 100644 index 000000000..f1c403722 --- /dev/null +++ b/test/settings.hpp @@ -0,0 +1,36 @@ +/* + +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/settings_pack.hpp" + +libtorrent::settings_pack settings(); +