diff --git a/ChangeLog b/ChangeLog index 7a3245e3d..3d017fe67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -84,6 +84,8 @@ * resume data no longer has timestamps of files * require C++11 to build libtorrent + * fixed race condition in random number generator + * fix race condition in stat_cache (disk storage) * improve error handling of failing to change file priority The API for custom storage implementations was altered * set the hidden attribute when creating the part file diff --git a/include/libtorrent/alert_manager.hpp b/include/libtorrent/alert_manager.hpp index 587c42d39..9a0808d46 100644 --- a/include/libtorrent/alert_manager.hpp +++ b/include/libtorrent/alert_manager.hpp @@ -73,8 +73,8 @@ namespace libtorrent { // don't add more than this number of alerts, unless it's a // high priority alert, in which case we try harder to deliver it // for high priority alerts, double the upper limit - if (m_alerts[m_generation].size() >= m_queue_size_limit - * (1 + T::priority)) + if (m_alerts[m_generation].size() / (1 + T::priority) + >= m_queue_size_limit) { // record that we dropped an alert of this type m_dropped.set(T::alert_type); diff --git a/include/libtorrent/stat_cache.hpp b/include/libtorrent/stat_cache.hpp index 43d33a910..1eae61fb6 100644 --- a/include/libtorrent/stat_cache.hpp +++ b/include/libtorrent/stat_cache.hpp @@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "libtorrent/config.hpp" #include "libtorrent/error_code.hpp" @@ -74,6 +75,9 @@ namespace libtorrent { private: + void set_cache_impl(file_index_t i, std::int64_t size); + void set_error_impl(file_index_t i, error_code const& ec); + // returns the index to the specified error. Either an existing one or a // newly added entry int add_error(error_code const& ec); @@ -90,6 +94,8 @@ namespace libtorrent { std::int64_t file_size; }; + mutable std::mutex m_mutex; + // one entry per file aux::vector m_stat_cache; diff --git a/src/random.cpp b/src/random.cpp index d322196fc..42c7667ce 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -36,6 +36,10 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/aux_/openssl.hpp" #include "libtorrent/aux_/throw.hpp" +#if defined BOOST_NO_CXX11_THREAD_LOCAL +#include +#endif + #if TORRENT_USE_CRYPTOAPI #include "libtorrent/aux_/win_crypto_provider.hpp" @@ -54,6 +58,15 @@ extern "C" { #include "libtorrent/aux_/dev_random.hpp" #endif +#ifdef BOOST_NO_CXX11_THREAD_LOCAL +namespace { + // if the random number generator can't be thread local, just protect it with + // a mutex. Not ideal, but hopefully not too many people are affected by old + // systems + std::mutex rng_mutex; +} +#endif + namespace libtorrent { namespace aux { std::mt19937& random_engine() @@ -62,8 +75,13 @@ namespace libtorrent { namespace aux { // make sure random numbers are deterministic. Seed with a fixed number static std::mt19937 rng(0x82daf973); #else + static std::random_device dev; +#ifdef BOOST_NO_CXX11_THREAD_LOCAL static std::mt19937 rng(dev()); +#else + thread_local static std::mt19937 rng(dev()); +#endif #endif return rng; } @@ -102,6 +120,9 @@ namespace libtorrent { namespace aux { std::uint32_t random(std::uint32_t const max) { +#ifdef BOOST_NO_CXX11_THREAD_LOCAL + std::lock_guard l(rng_mutex); +#endif return std::uniform_int_distribution(0, max)(aux::random_engine()); } } diff --git a/src/stat_cache.cpp b/src/stat_cache.cpp index d1df6b338..394f67e94 100644 --- a/src/stat_cache.cpp +++ b/src/stat_cache.cpp @@ -41,6 +41,12 @@ namespace libtorrent { stat_cache::~stat_cache() = default; void stat_cache::set_cache(file_index_t const i, std::int64_t const size) + { + std::lock_guard l(m_mutex); + set_cache_impl(i, size); + } + + void stat_cache::set_cache_impl(file_index_t const i, std::int64_t const size) { if (i >= m_stat_cache.end_index()) m_stat_cache.resize(static_cast(i) + 1, not_in_cache); @@ -48,6 +54,12 @@ namespace libtorrent { } void stat_cache::set_error(file_index_t const i, error_code const& ec) + { + std::lock_guard l(m_mutex); + set_error_impl(i, ec); + } + + void stat_cache::set_error_impl(file_index_t const i, error_code const& ec) { if (i >= m_stat_cache.end_index()) m_stat_cache.resize(static_cast(i) + 1, not_in_cache); @@ -58,6 +70,7 @@ namespace libtorrent { void stat_cache::set_dirty(file_index_t const i) { + std::lock_guard l(m_mutex); if (i >= m_stat_cache.end_index()) return; m_stat_cache[i].file_size = not_in_cache; } @@ -65,6 +78,7 @@ namespace libtorrent { std::int64_t stat_cache::get_filesize(file_index_t const i, file_storage const& fs , std::string const& save_path, error_code& ec) { + std::lock_guard l(m_mutex); TORRENT_ASSERT(i < fs.end_file()); if (i >= m_stat_cache.end_index()) m_stat_cache.resize(static_cast(i) + 1, not_in_cache); std::int64_t sz = m_stat_cache[i].file_size; @@ -81,12 +95,12 @@ namespace libtorrent { stat_file(file_path, &s, ec); if (ec) { - set_error(i, ec); + set_error_impl(i, ec); sz = file_error; } else { - set_cache(i, s.file_size); + set_cache_impl(i, s.file_size); sz = s.file_size; } } @@ -95,11 +109,13 @@ namespace libtorrent { void stat_cache::reserve(int num_files) { + std::lock_guard l(m_mutex); m_stat_cache.resize(num_files, not_in_cache); } void stat_cache::clear() { + std::lock_guard l(m_mutex); m_stat_cache.clear(); m_stat_cache.shrink_to_fit(); m_errors.clear(); diff --git a/test/test_alert_manager.cpp b/test/test_alert_manager.cpp index c9c4ccdd3..d5dbd457c 100644 --- a/test/test_alert_manager.cpp +++ b/test/test_alert_manager.cpp @@ -80,6 +80,25 @@ TORRENT_TEST(limit) TEST_EQUAL(alerts.size(), 201); } +TORRENT_TEST(limit_int_max) +{ + int const inf = std::numeric_limits::max(); + alert_manager mgr(inf, alert::all_categories); + + TEST_EQUAL(mgr.alert_queue_size_limit(), inf); + + for (piece_index_t i{0}; i < piece_index_t{600}; ++i) + mgr.emplace_alert(torrent_handle(), i); + + for (piece_index_t i{0}; i < piece_index_t{600}; ++i) + mgr.emplace_alert(torrent_handle(), sha1_hash()); + + std::vector alerts; + mgr.get_all(alerts); + + TEST_EQUAL(alerts.size(), 1200); +} + TORRENT_TEST(priority_limit) { alert_manager mgr(100, alert::all_categories);