From 1d1398ed7f2b4e73c13a7264ae9ae9c0f7fe2f9a Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 24 Feb 2008 23:14:10 +0000 Subject: [PATCH] read cache fix --- include/libtorrent/disk_io_thread.hpp | 25 ++++++----- src/disk_io_thread.cpp | 63 +++++++++++++++------------ test/test_storage.cpp | 10 +++++ 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/include/libtorrent/disk_io_thread.hpp b/include/libtorrent/disk_io_thread.hpp index 9f5d97e6b..ef05b96cc 100644 --- a/include/libtorrent/disk_io_thread.hpp +++ b/include/libtorrent/disk_io_thread.hpp @@ -46,6 +46,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include "libtorrent/config.hpp" namespace libtorrent @@ -183,8 +184,6 @@ namespace libtorrent private: - typedef boost::recursive_mutex mutex_t; - struct cached_piece_entry { int piece; @@ -198,27 +197,33 @@ namespace libtorrent boost::shared_array blocks; }; + typedef boost::recursive_mutex mutex_t; + typedef std::list cache_t; + char* allocate_buffer(mutex_t::scoped_lock& l); void free_buffer(char* buf, mutex_t::scoped_lock& l); // cache operations - std::vector::iterator find_cached_piece( - std::vector& cache, disk_io_job const& j + cache_t::iterator find_cached_piece( + cache_t& cache, disk_io_job const& j , mutex_t::scoped_lock& l); // write cache operations void flush_oldest_piece(mutex_t::scoped_lock& l); void flush_expired_pieces(mutex_t::scoped_lock& l); - void flush_and_remove(std::vector::iterator i, mutex_t::scoped_lock& l); - void flush(std::vector::iterator i, mutex_t::scoped_lock& l); + void flush_and_remove(cache_t::iterator i, mutex_t::scoped_lock& l); + void flush(cache_t::iterator i, mutex_t::scoped_lock& l); void cache_block(disk_io_job& j, mutex_t::scoped_lock& l); // read cache operations - bool clear_oldest_read_piece(mutex_t::scoped_lock& l); + bool clear_oldest_read_piece(cache_t::iterator ignore + , mutex_t::scoped_lock& l); int read_into_piece(cached_piece_entry& p, int start_block, mutex_t::scoped_lock& l); int cache_read_block(disk_io_job const& j, mutex_t::scoped_lock& l); void free_piece(cached_piece_entry& p, mutex_t::scoped_lock& l); - bool make_room(int num_blocks, mutex_t::scoped_lock& l); + bool make_room(int num_blocks + , cache_t::iterator ignore + , mutex_t::scoped_lock& l); int try_read_from_cache(disk_io_job const& j, mutex_t::scoped_lock& l); mutable mutex_t m_mutex; @@ -228,10 +233,10 @@ namespace libtorrent size_type m_queue_buffer_size; // write cache - std::vector m_pieces; + cache_t m_pieces; // read cache - std::vector m_read_pieces; + cache_t m_read_pieces; // total number of blocks in use by both the read // and the write cache. This is not supposed to diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index db3df9a12..726fdd294 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -113,7 +113,7 @@ namespace libtorrent mutex_t::scoped_lock l(m_mutex); ret.clear(); ret.reserve(m_pieces.size()); - for (std::vector::const_iterator i = m_pieces.begin() + for (cache_t::const_iterator i = m_pieces.begin() , end(m_pieces.end()); i != end; ++i) { torrent_info const& ti = *i->storage->info(); @@ -200,12 +200,12 @@ namespace libtorrent } } - std::vector::iterator disk_io_thread::find_cached_piece( - std::vector& cache + disk_io_thread::cache_t::iterator disk_io_thread::find_cached_piece( + disk_io_thread::cache_t& cache , disk_io_job const& j, mutex_t::scoped_lock& l) { TORRENT_ASSERT(l.locked()); - for (std::vector::iterator i = cache.begin() + for (cache_t::iterator i = cache.begin() , end(cache.end()); i != end; ++i) { if (i->storage != j.storage || i->piece != j.piece) continue; @@ -222,7 +222,7 @@ namespace libtorrent INVARIANT_CHECK; for (;;) { - std::vector::iterator i = std::min_element( + cache_t::iterator i = std::min_element( m_pieces.begin(), m_pieces.end() , bind(&cached_piece_entry::last_use, _1) < bind(&cached_piece_entry::last_use, _2)); @@ -250,16 +250,20 @@ namespace libtorrent } } - bool disk_io_thread::clear_oldest_read_piece(mutex_t::scoped_lock& l) + bool disk_io_thread::clear_oldest_read_piece( + cache_t::iterator ignore + , mutex_t::scoped_lock& l) { INVARIANT_CHECK; - std::vector::iterator i = std::min_element( + cache_t::iterator i = std::min_element( m_read_pieces.begin(), m_read_pieces.end() , bind(&cached_piece_entry::last_use, _1) < bind(&cached_piece_entry::last_use, _2)); - if (i != m_read_pieces.end()) + if (i != m_read_pieces.end() && i != ignore) { + // don't replace an entry that is less than one second old + if (time_now() - i->last_use < seconds(1)) return false; free_piece(*i, l); m_read_pieces.erase(i); return true; @@ -273,9 +277,9 @@ namespace libtorrent INVARIANT_CHECK; // first look if there are any read cache entries that can // be cleared - if (clear_oldest_read_piece(l)) return; + if (clear_oldest_read_piece(m_read_pieces.end(), l)) return; - std::vector::iterator i = std::min_element( + cache_t::iterator i = std::min_element( m_pieces.begin(), m_pieces.end() , bind(&cached_piece_entry::last_use, _1) < bind(&cached_piece_entry::last_use, _2)); @@ -283,14 +287,14 @@ namespace libtorrent flush_and_remove(i, l); } - void disk_io_thread::flush_and_remove(std::vector::iterator e + void disk_io_thread::flush_and_remove(disk_io_thread::cache_t::iterator e , mutex_t::scoped_lock& l) { flush(e, l); m_pieces.erase(e); } - void disk_io_thread::flush(std::vector::iterator e + void disk_io_thread::flush(disk_io_thread::cache_t::iterator e , mutex_t::scoped_lock& l) { TORRENT_ASSERT(l.locked()); @@ -448,7 +452,9 @@ namespace libtorrent return (ret != buffer_size) ? -1 : ret; } - bool disk_io_thread::make_room(int num_blocks, mutex_t::scoped_lock& l) + bool disk_io_thread::make_room(int num_blocks + , cache_t::iterator ignore + , mutex_t::scoped_lock& l) { TORRENT_ASSERT(l.locked()); @@ -456,7 +462,7 @@ namespace libtorrent { // there's not enough room in the cache, clear a piece // from the read cache - if (!clear_oldest_read_piece(l)) return false; + if (!clear_oldest_read_piece(ignore, l)) return false; } return m_cache_size - m_cache_stats.cache_size >= num_blocks; @@ -475,7 +481,8 @@ namespace libtorrent int start_block = j.offset / m_block_size; - if (!make_room(blocks_in_piece - start_block, l)) return -2; + if (!make_room(blocks_in_piece - start_block + , m_read_pieces.end(), l)) return -2; cached_piece_entry p; p.piece = j.piece; @@ -498,7 +505,7 @@ namespace libtorrent void disk_io_thread::check_invariant() const { int cached_write_blocks = 0; - for (std::vector::const_iterator i = m_pieces.begin() + for (cache_t::const_iterator i = m_pieces.begin() , end(m_pieces.end()); i != end; ++i) { cached_piece_entry const& p = *i; @@ -520,7 +527,7 @@ namespace libtorrent } int cached_read_blocks = 0; - for (std::vector::const_iterator i = m_read_pieces.begin() + for (cache_t::const_iterator i = m_read_pieces.begin() , end(m_read_pieces.end()); i != end; ++i) { cached_piece_entry const& p = *i; @@ -557,7 +564,7 @@ namespace libtorrent if (!m_use_read_cache) return -2; - std::vector::iterator p + cache_t::iterator p = find_cached_piece(m_read_pieces, j, l); bool hit = true; @@ -571,7 +578,9 @@ namespace libtorrent ret = cache_read_block(j, l); hit = false; if (ret < 0) return ret; - p = m_read_pieces.end() - 1; + p = m_read_pieces.end(); + --p; + TORRENT_ASSERT(!m_read_pieces.empty()); TORRENT_ASSERT(p->piece == j.piece); TORRENT_ASSERT(p->storage == j.storage); } @@ -589,7 +598,7 @@ namespace libtorrent int blocks_in_piece = (piece_size + m_block_size - 1) / m_block_size; int end_block = block; while (end_block < blocks_in_piece && p->blocks[end_block] == 0) ++end_block; - if (!make_room(end_block - block, l)) return -2; + if (!make_room(end_block - block, p, l)) return -2; ret = read_into_piece(*p, block, l); hit = false; if (ret < 0) return ret; @@ -626,7 +635,7 @@ namespace libtorrent #ifndef NDEBUG if (j.action == disk_io_job::write) { - std::vector::iterator p + cache_t::iterator p = find_cached_piece(m_pieces, j, l); if (p != m_pieces.end()) { @@ -822,7 +831,7 @@ namespace libtorrent m_log << log_time() << " write " << j.buffer_size << std::endl; #endif mutex_t::scoped_lock l(m_mutex); - std::vector::iterator p + cache_t::iterator p = find_cached_piece(m_pieces, j, l); int block = j.offset / m_block_size; TORRENT_ASSERT(j.buffer); @@ -854,7 +863,7 @@ namespace libtorrent m_log << log_time() << " hash" << std::endl; #endif mutex_t::scoped_lock l(m_mutex); - std::vector::iterator i + cache_t::iterator i = find_cached_piece(m_pieces, j, l); if (i != m_pieces.end()) flush_and_remove(i, l); l.unlock(); @@ -893,10 +902,10 @@ namespace libtorrent #endif mutex_t::scoped_lock l(m_mutex); - std::vector::iterator i = std::remove_if( + cache_t::iterator i = std::remove_if( m_pieces.begin(), m_pieces.end(), bind(&cached_piece_entry::storage, _1) == j.storage); - for (std::vector::iterator k = i; k != m_pieces.end(); ++k) + for (cache_t::iterator k = i; k != m_pieces.end(); ++k) flush(k, l); m_pieces.erase(i, m_pieces.end()); m_pool.release_memory(); @@ -916,10 +925,10 @@ namespace libtorrent #endif mutex_t::scoped_lock l(m_mutex); - std::vector::iterator i = std::remove_if( + cache_t::iterator i = std::remove_if( m_pieces.begin(), m_pieces.end(), bind(&cached_piece_entry::storage, _1) == j.storage); - for (std::vector::iterator k = i; k != m_pieces.end(); ++k) + for (cache_t::iterator k = i; k != m_pieces.end(); ++k) { torrent_info const& ti = *k->storage->info(); int blocks_in_piece = (ti.piece_size(k->piece) + m_block_size - 1) / m_block_size; diff --git a/test/test_storage.cpp b/test/test_storage.cpp index 9901d1be1..aaca5f561 100644 --- a/test/test_storage.cpp +++ b/test/test_storage.cpp @@ -169,6 +169,16 @@ void test_remove(path const& test_path) TEST_CHECK(!exists(test_path / "temp_storage")); } +void test_fastresume() +{ + std::ofstream tf("temporary"); + boost::intrusive_ptr info = create_torrent(&tf); + + session ses; + torrent_handle h = ses.add_torrent(info, ); + +} + void run_test(path const& test_path) { std::cerr << "\n=== " << test_path.string() << " ===\n" << std::endl;