diff --git a/ChangeLog b/ChangeLog index 5e27efce2ae..be71e9d61a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * on windows, explicitly flush memory mapped files periodically * fix issue where incoming uTP connections were not accepted over SOCKS5 * fix several issues in handling of checking files of v2 torrents, esp. from magnet links * make the token limit when parsing metadata from magnet files configurable diff --git a/include/libtorrent/aux_/file_view_pool.hpp b/include/libtorrent/aux_/file_view_pool.hpp index a6ef66d14d0..597156dd032 100644 --- a/include/libtorrent/aux_/file_view_pool.hpp +++ b/include/libtorrent/aux_/file_view_pool.hpp @@ -110,6 +110,10 @@ namespace aux { void close_oldest(); +#if TORRENT_HAVE_MAP_VIEW_OF_FILE + int flush_next_file(); +#endif + private: std::shared_ptr remove_oldest(std::unique_lock&); @@ -150,6 +154,10 @@ namespace aux { mi::ordered_unique>, // look up files by least recently used mi::sequenced<> +#if TORRENT_HAVE_MAP_VIEW_OF_FILE + // look up files by least recently flushed + , mi::sequenced<> +#endif > >; diff --git a/include/libtorrent/aux_/mmap.hpp b/include/libtorrent/aux_/mmap.hpp index 254e122f3d1..4d18421c76e 100644 --- a/include/libtorrent/aux_/mmap.hpp +++ b/include/libtorrent/aux_/mmap.hpp @@ -122,6 +122,10 @@ namespace aux { #endif ); +#if TORRENT_HAVE_MAP_VIEW_OF_FILE + void flush(); +#endif + // non-copyable file_mapping(file_mapping const&) = delete; file_mapping& operator=(file_mapping const&) = delete; diff --git a/src/file_view_pool.cpp b/src/file_view_pool.cpp index 87fc467a83a..2e67c06e6cd 100644 --- a/src/file_view_pool.cpp +++ b/src/file_view_pool.cpp @@ -269,6 +269,20 @@ namespace libtorrent { namespace aux { std::unique_lock l(m_mutex); deferred_destruction = remove_oldest(l); } + +#if TORRENT_HAVE_MAP_VIEW_OF_FILE + int file_view_pool::flush_next_file() + { + std::unique_lock l(m_mutex); + auto& flush_view = m_files.get<2>(); + if (flush_view.size() == 0) return 0; + + flush_view.begin()->mapping->flush(); + flush_view.relocate(m_files.project<2>(flush_view.begin()), flush_view.end()); + + return static_cast(m_files.size()); + } +#endif } } diff --git a/src/mmap.cpp b/src/mmap.cpp index 836cbb9a1c6..4f823c6fcc5 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -638,9 +638,18 @@ file_mapping::file_mapping(file_handle file, open_mode_t const mode throw_ex(error_code(GetLastError(), system_category()), operation_t::file_mmap); } +void file_mapping::flush() +{ + if (m_mapping == nullptr) return; + + // ignore errors, this is best-effort + FlushViewOfFile(m_mapping, static_cast(m_size)); +} + void file_mapping::close() { if (m_mapping == nullptr) return; + flush(); std::lock_guard l(*m_open_unmap_lock); UnmapViewOfFile(m_mapping); m_mapping = nullptr; diff --git a/src/mmap_disk_io.cpp b/src/mmap_disk_io.cpp index 8899146f9f3..1599892cbad 100644 --- a/src/mmap_disk_io.cpp +++ b/src/mmap_disk_io.cpp @@ -314,10 +314,6 @@ struct TORRENT_EXTRA_EXPORT mmap_disk_io final settings_interface const& m_settings; - // we call close_oldest_file on the file_pool regularly. This is the next - // time we should call it - time_point m_next_close_oldest_file = min_time(); - // LRU cache of open files aux::file_view_pool m_file_pool; @@ -1510,6 +1506,14 @@ TORRENT_EXPORT std::unique_ptr mmap_disk_io_constructor( ++m_num_running_threads; m_stats_counters.inc_stats_counter(counters::num_running_threads, 1); + // we call close_oldest_file on the file_pool regularly. This is the next + // time we should call it + time_point next_close_oldest_file = min_time(); + +#if TORRENT_HAVE_MAP_VIEW_OF_FILE + time_point next_flush_file = min_time(); +#endif + for (;;) { aux::disk_io_job* j = nullptr; @@ -1538,20 +1542,35 @@ TORRENT_EXPORT std::unique_ptr mmap_disk_io_constructor( } } - if (now > m_next_close_oldest_file) + if (now > next_close_oldest_file) { seconds const interval(m_settings.get_int(settings_pack::close_file_interval)); if (interval <= seconds(0)) { // check again in one minute, in case the setting changed - m_next_close_oldest_file = now + minutes(1); + next_close_oldest_file = now + minutes(1); } else { - m_next_close_oldest_file = now + interval; + next_close_oldest_file = now + interval; m_file_pool.close_oldest(); } } + +#if TORRENT_HAVE_MAP_VIEW_OF_FILE + if (now > next_flush_file) + { + // on windows we need to explicitly ask the operating system to flush + // dirty pages from time to time + int const pool_size = m_file_pool.flush_next_file(); + + // aim at flushing every file every 30 seconds, without + // flushing a file more often that every other second. + auto const interval = std::max( + seconds(30) / std::max(1, pool_size), seconds(2)); + next_flush_file = now + ; + } +#endif } execute_job(j);