factor out disk_buffer_pool from disk_io_thread. refactored the file open modes to be platform independent. gave the disk_io_thread its own copy of session_settings that it shares with storage. added an unaligned_read implementation to storage. Added options to session_settings on when to open files in unbuffered mode. Added unit tests for unaligned reads
This commit is contained in:
parent
9a9f08f70f
commit
00808473e7
|
@ -41,20 +41,20 @@ namespace libtorrent
|
|||
{
|
||||
|
||||
namespace aux { class session_impl; }
|
||||
class disk_io_thread;
|
||||
class disk_buffer_pool;
|
||||
|
||||
struct TORRENT_EXPORT disk_buffer_holder
|
||||
{
|
||||
disk_buffer_holder(aux::session_impl& ses, char* buf);
|
||||
disk_buffer_holder(disk_io_thread& iothread, char* buf);
|
||||
disk_buffer_holder(disk_io_thread& iothread, char* buf, int num_blocks);
|
||||
disk_buffer_holder(disk_buffer_pool& disk_pool, char* buf);
|
||||
disk_buffer_holder(disk_buffer_pool& disk_pool, char* buf, int num_blocks);
|
||||
~disk_buffer_holder();
|
||||
char* release();
|
||||
char* get() const { return m_buf; }
|
||||
void reset(char* buf = 0, int num_blocks = 1);
|
||||
void swap(disk_buffer_holder& h)
|
||||
{
|
||||
TORRENT_ASSERT(&h.m_iothread == &m_iothread);
|
||||
TORRENT_ASSERT(&h.m_disk_pool == &m_disk_pool);
|
||||
std::swap(h.m_buf, m_buf);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ namespace libtorrent
|
|||
{ return m_buf == 0? 0: &disk_buffer_holder::release; }
|
||||
|
||||
private:
|
||||
disk_io_thread& m_iothread;
|
||||
disk_buffer_pool& m_disk_pool;
|
||||
char* m_buf;
|
||||
int m_num_blocks;
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
#include <boost/pool/pool.hpp>
|
||||
#endif
|
||||
#include "libtorrent/session_settings.hpp"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
@ -156,18 +157,56 @@ namespace libtorrent
|
|||
int read_cache_size;
|
||||
};
|
||||
|
||||
// this is a singleton consisting of the thread and a queue
|
||||
// of disk io jobs
|
||||
struct disk_io_thread : boost::noncopyable
|
||||
struct disk_buffer_pool : boost::noncopyable
|
||||
{
|
||||
disk_io_thread(io_service& ios, int block_size = 16 * 1024);
|
||||
~disk_io_thread();
|
||||
disk_buffer_pool(int block_size);
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
bool is_disk_buffer(char* buffer) const;
|
||||
#endif
|
||||
|
||||
char* allocate_buffer();
|
||||
void free_buffer(char* buf);
|
||||
|
||||
char* allocate_buffers(int blocks);
|
||||
void free_buffers(char* buf, int blocks);
|
||||
|
||||
int block_size() const { return m_block_size; }
|
||||
|
||||
#ifdef TORRENT_STATS
|
||||
int disk_allocations() const
|
||||
{ return m_allocations; }
|
||||
#endif
|
||||
|
||||
void release_memory();
|
||||
|
||||
// number of bytes per block. The BitTorrent
|
||||
// protocol defines the block size to 16 KiB.
|
||||
const int m_block_size;
|
||||
|
||||
private:
|
||||
|
||||
// this only protects the pool allocator
|
||||
typedef boost::mutex mutex_t;
|
||||
mutable mutex_t m_pool_mutex;
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
// memory pool for read and write operations
|
||||
// and disk cache
|
||||
boost::pool<page_aligned_allocator> m_pool;
|
||||
#endif
|
||||
|
||||
#ifdef TORRENT_STATS
|
||||
int m_allocations;
|
||||
#endif
|
||||
};
|
||||
|
||||
// this is a singleton consisting of the thread and a queue
|
||||
// of disk io jobs
|
||||
struct disk_io_thread : disk_buffer_pool
|
||||
{
|
||||
disk_io_thread(io_service& ios, int block_size = 16 * 1024);
|
||||
~disk_io_thread();
|
||||
|
||||
void join();
|
||||
|
||||
// aborts read operations
|
||||
|
@ -190,23 +229,10 @@ namespace libtorrent
|
|||
|
||||
void operator()();
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
bool is_disk_buffer(char* buffer) const;
|
||||
#endif
|
||||
|
||||
char* allocate_buffer();
|
||||
void free_buffer(char* buf);
|
||||
|
||||
char* allocate_buffers(int blocks);
|
||||
void free_buffers(char* buf, int blocks);
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
void check_invariant() const;
|
||||
#endif
|
||||
|
||||
int block_size() const { return m_block_size; }
|
||||
bool no_buffer() const { return m_disk_io_no_buffer; }
|
||||
|
||||
private:
|
||||
|
||||
struct cached_piece_entry
|
||||
|
@ -272,45 +298,11 @@ namespace libtorrent
|
|||
cache_status m_cache_stats;
|
||||
int m_num_cached_blocks;
|
||||
|
||||
// in (16kB) blocks
|
||||
int m_cache_size;
|
||||
|
||||
// expiration time of cache entries in seconds
|
||||
int m_cache_expiry;
|
||||
|
||||
// if set to true, each piece flush will allocate
|
||||
// one piece worth of temporary memory on the heap
|
||||
// and copy the block data into it, and then perform
|
||||
// a single write operation from that buffer.
|
||||
// if memory is constrained, that temporary buffer
|
||||
// might is avoided by setting this to false.
|
||||
// in case the allocation fails, the piece flush
|
||||
// falls back to writing each block separately.
|
||||
bool m_coalesce_writes;
|
||||
bool m_coalesce_reads;
|
||||
|
||||
bool m_use_read_cache;
|
||||
bool m_disk_io_no_buffer;
|
||||
|
||||
// this only protects the pool allocator
|
||||
mutable mutex_t m_pool_mutex;
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
// memory pool for read and write operations
|
||||
// and disk cache
|
||||
boost::pool<page_aligned_allocator> m_pool;
|
||||
#endif
|
||||
|
||||
// number of bytes per block. The BitTorrent
|
||||
// protocol defines the block size to 16 KiB.
|
||||
int m_block_size;
|
||||
session_settings m_settings;
|
||||
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
std::ofstream m_log;
|
||||
#endif
|
||||
#ifdef TORRENT_STATS
|
||||
int m_allocations;
|
||||
#endif
|
||||
|
||||
size_type m_writes;
|
||||
size_type m_blocks_written;
|
||||
|
||||
|
|
|
@ -86,28 +86,18 @@ namespace libtorrent
|
|||
{
|
||||
// when a file is opened with no_buffer
|
||||
// file offsets have to be aligned to
|
||||
// pages and buffer addresses and sizes
|
||||
// have to be page aligned too
|
||||
#ifdef TORRENT_WINDOWS
|
||||
read_only = GENERIC_READ,
|
||||
write_only = GENERIC_WRITE,
|
||||
read_write = GENERIC_READ | GENERIC_WRITE,
|
||||
no_buffer = 1,
|
||||
attribute_hidden = FILE_ATTRIBUTE_HIDDEN,
|
||||
attribute_executable = 0,
|
||||
#else
|
||||
read_only = O_RDONLY,
|
||||
write_only = O_WRONLY | O_CREAT,
|
||||
read_write = O_RDWR | O_CREAT,
|
||||
#if defined O_DIRECT
|
||||
no_buffer = O_DIRECT,
|
||||
#else
|
||||
no_buffer = O_SYNC,
|
||||
#endif
|
||||
attribute_hidden = 0,
|
||||
attribute_executable = 0x100000,
|
||||
#endif
|
||||
// pos_alignment() and buffer addresses
|
||||
// to buf_alignment() and read/write sizes
|
||||
// to size_alignment()
|
||||
read_only = 0,
|
||||
write_only = 1,
|
||||
read_write = 2,
|
||||
rw_mask = read_only | write_only | read_write,
|
||||
no_buffer = 4,
|
||||
mode_mask = rw_mask | no_buffer,
|
||||
|
||||
attribute_hidden = 0x1000,
|
||||
attribute_executable = 0x2000,
|
||||
attribute_mask = attribute_hidden | attribute_executable
|
||||
};
|
||||
|
||||
|
@ -142,6 +132,10 @@ namespace libtorrent
|
|||
// required alignment of buffer addresses
|
||||
int buf_alignment() const;
|
||||
|
||||
// read/write buffer sizes needs to be aligned to
|
||||
// this when in unbuffered mode
|
||||
int size_alignment() const;
|
||||
|
||||
size_type writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec);
|
||||
size_type readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec);
|
||||
|
||||
|
|
|
@ -125,7 +125,8 @@ namespace libtorrent
|
|||
, cache_size(512)
|
||||
, cache_expiry(60)
|
||||
, use_read_cache(true)
|
||||
, disk_io_no_buffer(true)
|
||||
, disk_io_write_mode(0)
|
||||
, disk_io_read_mode(0)
|
||||
, outgoing_ports(0,0)
|
||||
, peer_tos(0)
|
||||
, active_downloads(8)
|
||||
|
@ -378,9 +379,17 @@ namespace libtorrent
|
|||
// cache for caching blocks read from disk too
|
||||
bool use_read_cache;
|
||||
|
||||
// when set to true, files that can be opened with
|
||||
// no OS buffering will be opened that way
|
||||
bool disk_io_no_buffer;
|
||||
enum io_buffer_mode_t
|
||||
{
|
||||
enable_os_cache = 0,
|
||||
disable_os_cache_for_aligned_files = 1,
|
||||
disable_os_cache = 2
|
||||
};
|
||||
int disk_io_write_mode:4;
|
||||
int disk_io_read_mode:4;
|
||||
|
||||
bool coalesce_reads;
|
||||
bool coalesce_writes;
|
||||
|
||||
// if != (0, 0), this is the range of ports that
|
||||
// outgoing connections will be bound to. This
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace libtorrent
|
|||
class session;
|
||||
struct file_pool;
|
||||
struct disk_io_job;
|
||||
struct disk_buffer_pool;
|
||||
|
||||
enum storage_mode_t
|
||||
{
|
||||
|
@ -116,7 +117,7 @@ namespace libtorrent
|
|||
|
||||
struct TORRENT_EXPORT storage_interface
|
||||
{
|
||||
storage_interface(): m_io_thread(0) {}
|
||||
storage_interface(): m_disk_pool(0), m_settings(0) {}
|
||||
// create directories and set file sizes
|
||||
// if allocate_files is true.
|
||||
// allocate_files is true if allocation mode
|
||||
|
@ -168,7 +169,8 @@ namespace libtorrent
|
|||
// non-zero return value indicates an error
|
||||
virtual bool delete_files() = 0;
|
||||
|
||||
disk_io_thread* io_thread() { return m_io_thread; }
|
||||
disk_buffer_pool* disk_pool() { return m_disk_pool; }
|
||||
session_settings const& settings() const { return *m_settings; }
|
||||
|
||||
void set_error(boost::filesystem::path const& file, error_code const& ec) const
|
||||
{
|
||||
|
@ -185,7 +187,8 @@ namespace libtorrent
|
|||
|
||||
virtual ~storage_interface() {}
|
||||
|
||||
disk_io_thread* m_io_thread;
|
||||
disk_buffer_pool* m_disk_pool;
|
||||
session_settings* m_settings;
|
||||
};
|
||||
|
||||
typedef storage_interface* (&storage_constructor_type)(
|
||||
|
|
|
@ -38,29 +38,29 @@ namespace libtorrent
|
|||
{
|
||||
|
||||
disk_buffer_holder::disk_buffer_holder(aux::session_impl& ses, char* buf)
|
||||
: m_iothread(ses.m_disk_thread), m_buf(buf), m_num_blocks(1)
|
||||
: m_disk_pool(ses.m_disk_thread), m_buf(buf), m_num_blocks(1)
|
||||
{
|
||||
TORRENT_ASSERT(buf == 0 || m_iothread.is_disk_buffer(buf));
|
||||
TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf));
|
||||
}
|
||||
|
||||
disk_buffer_holder::disk_buffer_holder(disk_io_thread& iothread, char* buf)
|
||||
: m_iothread(iothread), m_buf(buf), m_num_blocks(1)
|
||||
disk_buffer_holder::disk_buffer_holder(disk_buffer_pool& iothread, char* buf)
|
||||
: m_disk_pool(iothread), m_buf(buf), m_num_blocks(1)
|
||||
{
|
||||
TORRENT_ASSERT(buf == 0 || m_iothread.is_disk_buffer(buf));
|
||||
TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf));
|
||||
}
|
||||
|
||||
disk_buffer_holder::disk_buffer_holder(disk_io_thread& iothread, char* buf, int num_blocks)
|
||||
: m_iothread(iothread), m_buf(buf), m_num_blocks(num_blocks)
|
||||
disk_buffer_holder::disk_buffer_holder(disk_buffer_pool& iothread, char* buf, int num_blocks)
|
||||
: m_disk_pool(iothread), m_buf(buf), m_num_blocks(num_blocks)
|
||||
{
|
||||
TORRENT_ASSERT(buf == 0 || m_iothread.is_disk_buffer(buf));
|
||||
TORRENT_ASSERT(buf == 0 || m_disk_pool.is_disk_buffer(buf));
|
||||
}
|
||||
|
||||
void disk_buffer_holder::reset(char* buf, int num_blocks)
|
||||
{
|
||||
if (m_buf)
|
||||
{
|
||||
if (m_num_blocks == 1) m_iothread.free_buffer(m_buf);
|
||||
else m_iothread.free_buffers(m_buf, m_num_blocks);
|
||||
if (m_num_blocks == 1) m_disk_pool.free_buffer(m_buf);
|
||||
else m_disk_pool.free_buffers(m_buf, m_num_blocks);
|
||||
}
|
||||
m_buf = buf;
|
||||
m_num_blocks = num_blocks;
|
||||
|
@ -77,8 +77,8 @@ namespace libtorrent
|
|||
{
|
||||
if (m_buf)
|
||||
{
|
||||
if (m_num_blocks == 1) m_iothread.free_buffer(m_buf);
|
||||
else m_iothread.free_buffers(m_buf, m_num_blocks);
|
||||
if (m_num_blocks == 1) m_disk_pool.free_buffer(m_buf);
|
||||
else m_disk_pool.free_buffers(m_buf, m_num_blocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,26 +44,102 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
namespace libtorrent
|
||||
{
|
||||
|
||||
disk_io_thread::disk_io_thread(asio::io_service& ios, int block_size)
|
||||
: m_abort(false)
|
||||
, m_queue_buffer_size(0)
|
||||
, m_cache_size(512) // 512 * 16kB = 8MB
|
||||
, m_cache_expiry(60) // 1 minute
|
||||
, m_coalesce_writes(false)
|
||||
, m_coalesce_reads(false)
|
||||
, m_use_read_cache(true)
|
||||
, m_disk_io_no_buffer(true)
|
||||
disk_buffer_pool::disk_buffer_pool(int block_size)
|
||||
: m_block_size(block_size)
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
, m_pool(block_size)
|
||||
#endif
|
||||
, m_block_size(block_size)
|
||||
, m_ios(ios)
|
||||
, m_disk_io_thread(boost::ref(*this))
|
||||
{
|
||||
#ifdef TORRENT_STATS
|
||||
m_allocations = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
bool disk_buffer_pool::is_disk_buffer(char* buffer) const
|
||||
{
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
return true;
|
||||
#else
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
return m_pool.is_from(buffer);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
char* disk_buffer_pool::allocate_buffer()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
++m_allocations;
|
||||
#endif
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
return page_aligned_allocator::malloc(m_block_size);
|
||||
#else
|
||||
return (char*)m_pool.ordered_malloc();
|
||||
#endif
|
||||
}
|
||||
|
||||
void disk_buffer_pool::free_buffer(char* buf)
|
||||
{
|
||||
TORRENT_ASSERT(buf);
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
--m_allocations;
|
||||
#endif
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
page_aligned_allocator::free(buf);
|
||||
#else
|
||||
m_pool.ordered_free(buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
char* disk_buffer_pool::allocate_buffers(int num_blocks)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
m_allocations += num_blocks;
|
||||
#endif
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
return page_aligned_allocator::malloc(m_block_size * num_blocks);
|
||||
#else
|
||||
return (char*)m_pool.ordered_malloc(num_blocks);
|
||||
#endif
|
||||
}
|
||||
|
||||
void disk_buffer_pool::free_buffers(char* buf, int num_blocks)
|
||||
{
|
||||
TORRENT_ASSERT(buf);
|
||||
TORRENT_ASSERT(num_blocks >= 1);
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
m_allocations -= num_blocks;
|
||||
#endif
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
page_aligned_allocator::free(buf);
|
||||
#else
|
||||
m_pool.ordered_free(buf, num_blocks);
|
||||
#endif
|
||||
}
|
||||
|
||||
void disk_buffer_pool::release_memory()
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
m_pool.release_memory();
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------- disk_io_thread ------
|
||||
|
||||
|
||||
disk_io_thread::disk_io_thread(asio::io_service& ios, int block_size)
|
||||
: disk_buffer_pool(block_size)
|
||||
, m_abort(false)
|
||||
, m_queue_buffer_size(0)
|
||||
, m_ios(ios)
|
||||
, m_disk_io_thread(boost::ref(*this))
|
||||
{
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
m_log.open("disk_io_thread.log", std::ios::trunc);
|
||||
#endif
|
||||
|
@ -222,7 +298,7 @@ namespace libtorrent
|
|||
< bind(&cached_piece_entry::last_use, _2));
|
||||
if (i == m_pieces.end()) break;
|
||||
int age = total_seconds(now - i->last_use);
|
||||
if (age < m_cache_expiry) break;
|
||||
if (age < m_settings.cache_expiry) break;
|
||||
flush_and_remove(i, l);
|
||||
}
|
||||
|
||||
|
@ -235,7 +311,7 @@ namespace libtorrent
|
|||
< bind(&cached_piece_entry::last_use, _2));
|
||||
if (i == m_read_pieces.end()) break;
|
||||
int age = total_seconds(now - i->last_use);
|
||||
if (age < m_cache_expiry) break;
|
||||
if (age < m_settings.cache_expiry) break;
|
||||
free_piece(*i, l);
|
||||
m_read_pieces.erase(i);
|
||||
}
|
||||
|
@ -319,7 +395,7 @@ namespace libtorrent
|
|||
boost::scoped_array<char> buf;
|
||||
file::iovec_t* iov = 0;
|
||||
int iov_counter = 0;
|
||||
if (m_coalesce_writes) buf.reset(new (std::nothrow) char[piece_size]);
|
||||
if (m_settings.coalesce_writes) buf.reset(new (std::nothrow) char[piece_size]);
|
||||
else iov = TORRENT_ALLOCA(file::iovec_t, blocks_in_piece);
|
||||
|
||||
for (int i = 0; i <= blocks_in_piece; ++i)
|
||||
|
@ -417,7 +493,7 @@ namespace libtorrent
|
|||
|
||||
int end_block = start_block;
|
||||
for (int i = start_block; i < blocks_in_piece
|
||||
&& m_cache_stats.cache_size < m_cache_size; ++i)
|
||||
&& m_cache_stats.cache_size < m_settings.cache_size; ++i)
|
||||
{
|
||||
// this is a block that is already allocated
|
||||
// stop allocating and don't read more than
|
||||
|
@ -444,7 +520,7 @@ namespace libtorrent
|
|||
boost::scoped_array<char> buf;
|
||||
boost::scoped_array<file::iovec_t> iov;
|
||||
int iov_counter = 0;
|
||||
if (m_coalesce_reads) buf.reset(new (std::nothrow) char[buffer_size]);
|
||||
if (m_settings.coalesce_reads) buf.reset(new (std::nothrow) char[buffer_size]);
|
||||
else iov.reset(new file::iovec_t[end_block - start_block]);
|
||||
|
||||
int ret = 0;
|
||||
|
@ -454,6 +530,7 @@ namespace libtorrent
|
|||
file::iovec_t b = { buf.get(), buffer_size };
|
||||
ret = p.storage->read_impl(&b, p.piece, start_block * m_block_size, 1);
|
||||
l.lock();
|
||||
TORRENT_ASSERT(ret == buffer_size || p.storage->error());
|
||||
if (p.storage->error()) { return -1; }
|
||||
++m_cache_stats.reads;
|
||||
}
|
||||
|
@ -486,6 +563,7 @@ namespace libtorrent
|
|||
l.unlock();
|
||||
ret = p.storage->read_impl(iov.get(), p.piece, start_block * m_block_size, iov_counter);
|
||||
l.lock();
|
||||
TORRENT_ASSERT(ret == buffer_size || p.storage->error());
|
||||
if (p.storage->error()) { return -1; }
|
||||
++m_cache_stats.reads;
|
||||
}
|
||||
|
@ -499,14 +577,14 @@ namespace libtorrent
|
|||
, cache_t::iterator ignore
|
||||
, mutex_t::scoped_lock& l)
|
||||
{
|
||||
if (m_cache_size - m_cache_stats.cache_size < num_blocks)
|
||||
if (m_settings.cache_size - m_cache_stats.cache_size < num_blocks)
|
||||
{
|
||||
// there's not enough room in the cache, clear a piece
|
||||
// from the read cache
|
||||
if (!clear_oldest_read_piece(ignore, l)) return false;
|
||||
}
|
||||
|
||||
return m_cache_size - m_cache_stats.cache_size >= num_blocks;
|
||||
return m_settings.cache_size - m_cache_stats.cache_size >= num_blocks;
|
||||
}
|
||||
|
||||
// returns -1 on read error, -2 if there isn't any space in the cache
|
||||
|
@ -597,7 +675,7 @@ namespace libtorrent
|
|||
|
||||
// when writing, there may be a one block difference, right before an old piece
|
||||
// is flushed
|
||||
TORRENT_ASSERT(m_cache_stats.cache_size <= m_cache_size + 1);
|
||||
TORRENT_ASSERT(m_cache_stats.cache_size <= m_settings.cache_size + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -606,7 +684,7 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(j.buffer);
|
||||
|
||||
mutex_t::scoped_lock l(m_piece_mutex);
|
||||
if (!m_use_read_cache) return -2;
|
||||
if (!m_settings.use_read_cache) return -2;
|
||||
|
||||
cache_t::iterator p
|
||||
= find_cached_piece(m_read_pieces, j, l);
|
||||
|
@ -729,73 +807,6 @@ namespace libtorrent
|
|||
m_signal.notify_all();
|
||||
}
|
||||
|
||||
#ifdef TORRENT_DEBUG
|
||||
bool disk_io_thread::is_disk_buffer(char* buffer) const
|
||||
{
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
return true;
|
||||
#else
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
return m_pool.is_from(buffer);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
char* disk_io_thread::allocate_buffer()
|
||||
{
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
++m_allocations;
|
||||
#endif
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
return page_aligned_allocator::malloc(m_block_size);
|
||||
#else
|
||||
return (char*)m_pool.ordered_malloc();
|
||||
#endif
|
||||
}
|
||||
|
||||
void disk_io_thread::free_buffer(char* buf)
|
||||
{
|
||||
TORRENT_ASSERT(buf);
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
--m_allocations;
|
||||
#endif
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
page_aligned_allocator::free(buf);
|
||||
#else
|
||||
m_pool.ordered_free(buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
char* disk_io_thread::allocate_buffers(int num_blocks)
|
||||
{
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
m_allocations += num_blocks;
|
||||
#endif
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
return page_aligned_allocator::malloc(m_block_size * num_blocks);
|
||||
#else
|
||||
return (char*)m_pool.ordered_malloc(num_blocks);
|
||||
#endif
|
||||
}
|
||||
|
||||
void disk_io_thread::free_buffers(char* buf, int num_blocks)
|
||||
{
|
||||
TORRENT_ASSERT(buf);
|
||||
TORRENT_ASSERT(num_blocks >= 1);
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
m_allocations -= num_blocks;
|
||||
#endif
|
||||
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
page_aligned_allocator::free(buf);
|
||||
#else
|
||||
m_pool.ordered_free(buf, num_blocks);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool disk_io_thread::test_error(disk_io_job& j)
|
||||
{
|
||||
error_code const& ec = j.storage->error();
|
||||
|
@ -868,6 +879,9 @@ namespace libtorrent
|
|||
try {
|
||||
#endif
|
||||
|
||||
if (j.storage && j.storage->get_storage_impl()->m_settings == 0)
|
||||
j.storage->get_storage_impl()->m_settings = &m_settings;
|
||||
|
||||
switch (j.action)
|
||||
{
|
||||
case disk_io_job::update_settings:
|
||||
|
@ -880,11 +894,7 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(s.cache_size >= 0);
|
||||
TORRENT_ASSERT(s.cache_expiry > 0);
|
||||
|
||||
mutex_t::scoped_lock l(m_piece_mutex);
|
||||
m_cache_size = s.cache_size;
|
||||
m_cache_expiry = s.cache_expiry;
|
||||
m_use_read_cache = s.use_read_cache;
|
||||
m_disk_io_no_buffer = s.disk_io_no_buffer;
|
||||
m_settings = s;
|
||||
}
|
||||
case disk_io_job::abort_torrent:
|
||||
{
|
||||
|
@ -1024,7 +1034,7 @@ namespace libtorrent
|
|||
// in the cache, we should not
|
||||
// free it at the end
|
||||
holder.release();
|
||||
if (m_cache_stats.cache_size >= m_cache_size)
|
||||
if (m_cache_stats.cache_size >= m_settings.cache_size)
|
||||
flush_oldest_piece(l);
|
||||
break;
|
||||
}
|
||||
|
@ -1098,12 +1108,8 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
l.unlock();
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
{
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
m_pool.release_memory();
|
||||
}
|
||||
#endif
|
||||
release_memory();
|
||||
|
||||
ret = j.storage->release_files_impl();
|
||||
if (ret != 0) test_error(j);
|
||||
break;
|
||||
|
@ -1132,12 +1138,7 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
l.unlock();
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
{
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
m_pool.release_memory();
|
||||
}
|
||||
#endif
|
||||
release_memory();
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -1168,12 +1169,8 @@ namespace libtorrent
|
|||
}
|
||||
m_pieces.erase(i, m_pieces.end());
|
||||
l.unlock();
|
||||
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR
|
||||
{
|
||||
mutex_t::scoped_lock l(m_pool_mutex);
|
||||
m_pool.release_memory();
|
||||
}
|
||||
#endif
|
||||
release_memory();
|
||||
|
||||
ret = j.storage->delete_files_impl();
|
||||
if (ret != 0) test_error(j);
|
||||
break;
|
||||
|
|
81
src/file.cpp
81
src/file.cpp
|
@ -142,22 +142,49 @@ namespace libtorrent
|
|||
close();
|
||||
#ifdef TORRENT_WINDOWS
|
||||
|
||||
struct open_mode_t
|
||||
{
|
||||
DWORD rw_mode;
|
||||
DWORD share_mode;
|
||||
DWORD create_mode;
|
||||
DWORD flags;
|
||||
};
|
||||
|
||||
const static open_mode_t mode_array[] =
|
||||
{
|
||||
// read_only
|
||||
{GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS},
|
||||
// write_only
|
||||
{GENERIC_WRITE, FILE_SHARE_READ, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS},
|
||||
// read_write
|
||||
{GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS},
|
||||
// read_only no_buffer
|
||||
{GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING },
|
||||
// write_only no_buffer
|
||||
{GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING },
|
||||
// read_write no_buffer
|
||||
{GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING },
|
||||
};
|
||||
|
||||
const static DWORD attrib_array[] =
|
||||
{
|
||||
FILE_ATTRIBUTE_NORMAL, // no attrib
|
||||
FILE_ATTRIBUTE_HIDDEN, // hidden
|
||||
FILE_ATTRIBUTE_NORMAL, // executable
|
||||
FILE_ATTRIBUTE_HIDDEN, // hidden + executable
|
||||
};
|
||||
|
||||
#ifdef TORRENT_USE_WPATH
|
||||
m_path = safe_convert(path.external_file_string());
|
||||
#else
|
||||
m_path = utf8_native(path.external_file_string());
|
||||
#endif
|
||||
|
||||
m_file_handle = CreateFile(
|
||||
m_path.c_str()
|
||||
, mode & rw_mask
|
||||
, FILE_SHARE_READ | ((mode & no_buffer) ? FILE_SHARE_WRITE : 0)
|
||||
, 0
|
||||
, ((mode & rw_mask) == read_write || (mode & rw_mask) == write_only)?OPEN_ALWAYS:OPEN_EXISTING
|
||||
, FILE_FLAG_RANDOM_ACCESS
|
||||
| ((mode & no_buffer)?FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING:0)
|
||||
| ((mode & attribute_mask)?(mode & attribute_mask):FILE_ATTRIBUTE_NORMAL)
|
||||
, 0);
|
||||
open_mode_t const& m = mode_array[mode & mode_mask];
|
||||
DWORD a = attrib_array[(mode & attribute_mask) >> 12];
|
||||
|
||||
m_file_handle = CreateFile(m_path.c_str(), m.rw_mode, m.share_mode, 0
|
||||
, m.create_mode, m.flags | (a ? a : FILE_ATTRIBUTE_NORMAL), 0);
|
||||
|
||||
if (m_file_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
|
@ -182,8 +209,14 @@ namespace libtorrent
|
|||
if (mode & attribute_executable)
|
||||
permissions |= S_IXGRP | S_IXOTH | S_IXUSR;
|
||||
|
||||
static const int mode_array[] = {O_RDONLY, O_WRONLY | O_CREAT, O_RDWR | O_CREAT};
|
||||
#ifdef O_DIRECT
|
||||
static const int no_buffer_flag[] = {0, O_DIRECT};
|
||||
#else
|
||||
static const int no_buffer_flag[] = {0, 0};
|
||||
#endif
|
||||
m_fd = ::open(path.external_file_string().c_str()
|
||||
, mode & (rw_mask | no_buffer), permissions);
|
||||
, mode_array[mode & rw_mask] | no_buffer_flag[(mode & no_buffer) >> 2], permissions);
|
||||
|
||||
#ifdef TORRENT_LINUX
|
||||
// workaround for linux bug
|
||||
|
@ -281,6 +314,16 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
int file::size_alignment() const
|
||||
{
|
||||
#if defined TORRENT_WINDOWS
|
||||
init_file();
|
||||
return m_page_size;
|
||||
#else
|
||||
return pos_alignment();
|
||||
#endif
|
||||
}
|
||||
|
||||
void file::close()
|
||||
{
|
||||
#if defined TORRENT_WINDOWS || defined TORRENT_LINUX
|
||||
|
@ -347,8 +390,8 @@ namespace libtorrent
|
|||
TORRENT_ASSERT((int(i->iov_base) & (buf_alignment()-1)) == 0);
|
||||
// every buffer must be a multiple of the page size
|
||||
// except for the last one
|
||||
TORRENT_ASSERT((i->iov_len & (pos_alignment()-1)) == 0 || i == end-1);
|
||||
if ((i->iov_len & (pos_alignment()-1)) != 0) eof = true;
|
||||
TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1);
|
||||
if ((i->iov_len & (size_alignment()-1)) != 0) eof = true;
|
||||
size += i->iov_len;
|
||||
}
|
||||
error_code code;
|
||||
|
@ -453,7 +496,7 @@ namespace libtorrent
|
|||
if (!aligned)
|
||||
{
|
||||
size = bufs_size(bufs, num_bufs);
|
||||
if (size & (pos_alignment()-1) == 0) aligned = true;
|
||||
if (size & (size_alignment()-1) == 0) aligned = true;
|
||||
}
|
||||
if (aligned)
|
||||
#endif
|
||||
|
@ -470,7 +513,7 @@ namespace libtorrent
|
|||
file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
||||
memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * num_bufs);
|
||||
iovec_t& last = temp_bufs[num_bufs-1];
|
||||
last.iov_len = (last.iov_len & ~(pos_alignment()-1)) + m_page_size;
|
||||
last.iov_len = (last.iov_len & ~(size_alignment()-1)) + m_page_size;
|
||||
ret = ::readv(m_fd, temp_bufs, num_bufs);
|
||||
if (ret < 0)
|
||||
{
|
||||
|
@ -507,8 +550,8 @@ namespace libtorrent
|
|||
TORRENT_ASSERT((int(i->iov_base) & (buf_alignment()-1)) == 0);
|
||||
// every buffer must be a multiple of the page size
|
||||
// except for the last one
|
||||
TORRENT_ASSERT((i->iov_len & (pos_alignment()-1)) == 0 || i == end-1);
|
||||
if ((i->iov_len & (pos_alignment()-1)) != 0) eof = true;
|
||||
TORRENT_ASSERT((i->iov_len & (size_alignment()-1)) == 0 || i == end-1);
|
||||
if ((i->iov_len & (size_alignment()-1)) != 0) eof = true;
|
||||
size += i->iov_len;
|
||||
}
|
||||
error_code code;
|
||||
|
@ -657,7 +700,7 @@ namespace libtorrent
|
|||
if (!aligned)
|
||||
{
|
||||
size = bufs_size(bufs, num_bufs);
|
||||
if (size & (pos_alignment()-1) == 0) aligned = true;
|
||||
if (size & (size_alignment()-1) == 0) aligned = true;
|
||||
}
|
||||
if (aligned)
|
||||
#endif
|
||||
|
@ -674,7 +717,7 @@ namespace libtorrent
|
|||
file::iovec_t* temp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
|
||||
memcpy(temp_bufs, bufs, sizeof(file::iovec_t) * num_bufs);
|
||||
iovec_t& last = temp_bufs[num_bufs-1];
|
||||
last.iov_len = (last.iov_len & ~(pos_alignment()-1)) + pos_alignment();
|
||||
last.iov_len = (last.iov_len & ~(size_alignment()-1)) + size_alignment();
|
||||
ret = ::writev(m_fd, temp_bufs, num_bufs);
|
||||
if (ret < 0)
|
||||
{
|
||||
|
|
|
@ -455,6 +455,7 @@ namespace libtorrent
|
|||
size_type (storage::*unaligned_op)(boost::shared_ptr<file> const& f
|
||||
, size_type file_offset, file::iovec_t const* bufs, int num_bufs
|
||||
, error_code& ec);
|
||||
int cache_setting;
|
||||
int mode;
|
||||
};
|
||||
|
||||
|
@ -491,13 +492,13 @@ namespace libtorrent
|
|||
if (slot_size > 0)
|
||||
{
|
||||
int block_size = 16 * 1024;
|
||||
if (io_thread()) block_size = io_thread()->block_size();
|
||||
if (disk_pool()) block_size = disk_pool()->block_size();
|
||||
int size = slot_size;
|
||||
int num_blocks = (size + block_size - 1) / block_size;
|
||||
file::iovec_t* bufs = TORRENT_ALLOCA(file::iovec_t, num_blocks);
|
||||
for (int i = 0; i < num_blocks; ++i)
|
||||
{
|
||||
bufs[i].iov_base = io_thread()->allocate_buffer();
|
||||
bufs[i].iov_base = disk_pool()->allocate_buffer();
|
||||
bufs[i].iov_len = (std::min)(block_size, size);
|
||||
size -= bufs[i].iov_len;
|
||||
}
|
||||
|
@ -506,7 +507,7 @@ namespace libtorrent
|
|||
for (int i = 0; i < num_blocks; ++i)
|
||||
{
|
||||
ph.h.update((char const*)bufs[i].iov_base, bufs[i].iov_len);
|
||||
io_thread()->free_buffer((char*)bufs[i].iov_base);
|
||||
disk_pool()->free_buffer((char*)bufs[i].iov_base);
|
||||
}
|
||||
if (error()) return sha1_hash(0);
|
||||
}
|
||||
|
@ -564,9 +565,10 @@ namespace libtorrent
|
|||
{
|
||||
error_code ec;
|
||||
int mode = file::read_write;
|
||||
if (io_thread()
|
||||
&& io_thread()->no_buffer()
|
||||
&& ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0)
|
||||
if (m_settings
|
||||
&& (settings().disk_io_read_mode == session_settings::disable_os_cache
|
||||
|| (settings().disk_io_read_mode == session_settings::disable_os_cache_for_aligned_files
|
||||
&& ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0)))
|
||||
mode |= file::no_buffer;
|
||||
boost::shared_ptr<file> f = m_pool.open_file(this
|
||||
, m_save_path / file_iter->path, mode, ec);
|
||||
|
@ -954,22 +956,22 @@ namespace libtorrent
|
|||
#endif
|
||||
|
||||
#define TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size) \
|
||||
int num_blocks = (piece_size + io_thread()->block_size() - 1) / io_thread()->block_size(); \
|
||||
int num_blocks = (piece_size + disk_pool()->block_size() - 1) / disk_pool()->block_size(); \
|
||||
file::iovec_t* bufs = TORRENT_ALLOCA(file::iovec_t, num_blocks); \
|
||||
for (int i = 0, size = piece_size; i < num_blocks; ++i) \
|
||||
{ \
|
||||
bufs[i].iov_base = io_thread()->allocate_buffer(); \
|
||||
bufs[i].iov_len = (std::min)(io_thread()->block_size(), size); \
|
||||
bufs[i].iov_base = disk_pool()->allocate_buffer(); \
|
||||
bufs[i].iov_len = (std::min)(disk_pool()->block_size(), size); \
|
||||
size -= bufs[i].iov_len; \
|
||||
}
|
||||
|
||||
#define TORRENT_FREE_BLOCKS(bufs, num_blocks) \
|
||||
for (int i = 0; i < num_blocks; ++i) \
|
||||
io_thread()->free_buffer((char*)bufs[i].iov_base);
|
||||
disk_pool()->free_buffer((char*)bufs[i].iov_base);
|
||||
|
||||
#define TORRENT_SET_SIZE(bufs, size, num_bufs) \
|
||||
for (num_bufs = 0; size > 0; size -= io_thread()->block_size(), ++num_bufs) \
|
||||
bufs[num_bufs].iov_len = (std::min)(io_thread()->block_size(), size)
|
||||
for (num_bufs = 0; size > 0; size -= disk_pool()->block_size(), ++num_bufs) \
|
||||
bufs[num_bufs].iov_len = (std::min)(disk_pool()->block_size(), size)
|
||||
|
||||
|
||||
bool storage::move_slot(int src_slot, int dst_slot)
|
||||
|
@ -1044,14 +1046,16 @@ ret:
|
|||
int storage::writev(file::iovec_t const* bufs, int slot, int offset
|
||||
, int num_bufs)
|
||||
{
|
||||
fileop op = { &file::writev, &storage::write_unaligned, file::read_write };
|
||||
fileop op = { &file::writev, &storage::write_unaligned
|
||||
, m_settings ? settings().disk_io_write_mode : 0, file::read_write };
|
||||
return readwritev(bufs, slot, offset, num_bufs, op);
|
||||
}
|
||||
|
||||
int storage::readv(file::iovec_t const* bufs, int slot, int offset
|
||||
, int num_bufs)
|
||||
{
|
||||
fileop op = { &file::readv, &storage::read_unaligned, file::read_only };
|
||||
fileop op = { &file::readv, &storage::read_unaligned
|
||||
, m_settings ? settings().disk_io_read_mode : 0, file::read_only };
|
||||
return readwritev(bufs, slot, offset, num_bufs, op);
|
||||
}
|
||||
|
||||
|
@ -1156,13 +1160,18 @@ ret:
|
|||
|
||||
error_code ec;
|
||||
int mode = op.mode;
|
||||
if (io_thread()
|
||||
&& io_thread()->no_buffer()
|
||||
&& ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0)
|
||||
|
||||
if (op.cache_setting == session_settings::disable_os_cache
|
||||
|| (op.cache_setting == session_settings::disable_os_cache_for_aligned_files
|
||||
&& ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0))
|
||||
{
|
||||
mode |= file::no_buffer;
|
||||
}
|
||||
|
||||
file_handle = m_pool.open_file(this, path, mode, ec);
|
||||
if (!file_handle || ec)
|
||||
{
|
||||
TORRENT_ASSERT(ec);
|
||||
set_error(path, ec);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1176,7 +1185,8 @@ ret:
|
|||
// special read that reads aligned buffers and copies
|
||||
// it into the one supplied
|
||||
if ((file_handle->open_mode() & file::no_buffer)
|
||||
&& ((file_iter->file_base + file_offset) & (file_handle->pos_alignment()-1)) != 0)
|
||||
&& (((file_iter->file_base + file_offset) & (file_handle->pos_alignment()-1)) != 0
|
||||
|| (int(tmp_bufs->iov_base) & (file_handle->buf_alignment()-1)) != 0))
|
||||
{
|
||||
bytes_transferred = (this->*op.unaligned_op)(file_handle, file_iter->file_base
|
||||
+ file_offset, tmp_bufs, num_tmp_bufs, ec);
|
||||
|
@ -1217,11 +1227,34 @@ ret:
|
|||
// for write cache).
|
||||
|
||||
// they read an unaligned buffer from a file that requires aligned access
|
||||
|
||||
size_type storage::read_unaligned(boost::shared_ptr<file> const& file_handle
|
||||
, size_type file_offset, file::iovec_t const* bufs, int num_bufs, error_code& ec)
|
||||
{
|
||||
TORRENT_ASSERT(false); // not implemented
|
||||
return 0;
|
||||
const int pos_align = file_handle->pos_alignment()-1;
|
||||
const int size_align = file_handle->size_alignment()-1;
|
||||
const int block_size = disk_pool()->block_size();
|
||||
|
||||
const int size = bufs_size(bufs, num_bufs);
|
||||
const int start_adjust = file_offset & pos_align;
|
||||
const int aligned_start = file_offset - start_adjust;
|
||||
const int aligned_size = ((size+start_adjust) & size_align)
|
||||
? ((size+start_adjust) & ~size_align) + size_align + 1 : size + start_adjust;
|
||||
const int num_blocks = (aligned_size + block_size - 1) / block_size;
|
||||
TORRENT_ASSERT((aligned_size & size_align) == 0);
|
||||
|
||||
disk_buffer_holder tmp_buf(*disk_pool(), disk_pool()->allocate_buffers(num_blocks), num_blocks);
|
||||
file::iovec_t b = {tmp_buf.get(), aligned_size};
|
||||
size_type ret = file_handle->readv(aligned_start, &b, 1, ec);
|
||||
if (ret < 0) return ret;
|
||||
char* read_buf = tmp_buf.get() + start_adjust;
|
||||
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i != end; ++i)
|
||||
{
|
||||
memcpy(i->iov_base, read_buf, i->iov_len);
|
||||
read_buf += i->iov_len;
|
||||
}
|
||||
if (ret < size + start_adjust) return ret - start_adjust;
|
||||
return size;
|
||||
}
|
||||
|
||||
size_type storage::write_unaligned(boost::shared_ptr<file> const& file_handle
|
||||
|
@ -1237,8 +1270,6 @@ ret:
|
|||
, int offset
|
||||
, int size)
|
||||
{
|
||||
// buffers must be page aligned
|
||||
TORRENT_ASSERT((int(buf) % 4096) == 0);
|
||||
file::iovec_t b = { (void*)buf, size };
|
||||
return writev(&b, slot, offset, 1);
|
||||
}
|
||||
|
@ -1249,8 +1280,6 @@ ret:
|
|||
, int offset
|
||||
, int size)
|
||||
{
|
||||
// buffers must be page aligned
|
||||
TORRENT_ASSERT((int(buf) % 4096) == 0);
|
||||
file::iovec_t b = { (void*)buf, size };
|
||||
return readv(&b, slot, offset, 1);
|
||||
}
|
||||
|
@ -1287,7 +1316,7 @@ ret:
|
|||
, m_io_thread(io)
|
||||
, m_torrent(torrent)
|
||||
{
|
||||
m_storage->m_io_thread = &m_io_thread;
|
||||
m_storage->m_disk_pool = &m_io_thread;
|
||||
}
|
||||
|
||||
piece_manager::~piece_manager()
|
||||
|
|
|
@ -49,21 +49,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
using namespace libtorrent;
|
||||
using namespace boost::filesystem;
|
||||
|
||||
const int piece_size = 16;
|
||||
const int piece_size = 16 * 1024;
|
||||
|
||||
const int half = piece_size / 2;
|
||||
|
||||
char piece0[piece_size] =
|
||||
{ 6, 6, 6, 6, 6, 6, 6, 6
|
||||
, 9, 9, 9, 9, 9, 9, 9, 9};
|
||||
|
||||
char piece1[piece_size] =
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0
|
||||
, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
|
||||
char piece2[piece_size] =
|
||||
{ 0, 0, 1, 0, 0, 0, 0, 0
|
||||
, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
char* piece0 = page_aligned_allocator::malloc(piece_size);
|
||||
char* piece1 = page_aligned_allocator::malloc(piece_size);
|
||||
char* piece2 = page_aligned_allocator::malloc(piece_size);
|
||||
|
||||
void on_read_piece(int ret, disk_io_job const& j, char const* data, int size)
|
||||
{
|
||||
|
@ -109,25 +101,42 @@ void on_move_storage(int ret, disk_io_job const& j, std::string path)
|
|||
void run_storage_tests(boost::intrusive_ptr<torrent_info> info
|
||||
, file_storage& fs
|
||||
, path const& test_path
|
||||
, libtorrent::storage_mode_t storage_mode)
|
||||
, libtorrent::storage_mode_t storage_mode
|
||||
, bool unbuffered)
|
||||
{
|
||||
TORRENT_ASSERT(fs.num_files() > 0);
|
||||
create_directory(test_path / "temp_storage");
|
||||
|
||||
int num_pieces = (1 + 612 + 17 + piece_size - 1) / piece_size;
|
||||
int num_pieces = fs.num_pieces();
|
||||
TEST_CHECK(info->num_pieces() == num_pieces);
|
||||
|
||||
char piece[piece_size];
|
||||
session_settings set;
|
||||
set.disk_io_write_mode = set.disk_io_read_mode
|
||||
= unbuffered ? session_settings::disable_os_cache_for_aligned_files
|
||||
: session_settings::enable_os_cache;
|
||||
|
||||
char* piece = page_aligned_allocator::malloc(piece_size);
|
||||
|
||||
{ // avoid having two storages use the same files
|
||||
file_pool fp;
|
||||
disk_buffer_pool dp(16 * 1024);
|
||||
boost::scoped_ptr<storage_interface> s(
|
||||
default_storage_constructor(fs, test_path, fp));
|
||||
s->m_settings = &set;
|
||||
s->m_disk_pool = &dp;
|
||||
|
||||
// write piece 1 (in slot 0)
|
||||
s->write(piece1, 0, 0, half);
|
||||
s->write(piece1 + half, 0, half, half);
|
||||
|
||||
// test unaligned read (where the bytes are aligned)
|
||||
s->read(piece + 3, 0, 3, piece_size-9);
|
||||
TEST_CHECK(std::equal(piece+3, piece + piece_size-9, piece1+3));
|
||||
|
||||
// test unaligned read (where the bytes are not aligned)
|
||||
s->read(piece, 0, 3, piece_size-9);
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size-9, piece1+3));
|
||||
|
||||
// verify piece 1
|
||||
TEST_CHECK(s->read(piece, 0, 0, piece_size) == piece_size);
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size, piece1));
|
||||
|
@ -142,7 +151,7 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
|
|||
|
||||
s->read(piece, 2, 0, piece_size);
|
||||
TEST_CHECK(std::equal(piece, piece + piece_size, piece2));
|
||||
|
||||
|
||||
s->release_files();
|
||||
}
|
||||
|
||||
|
@ -184,7 +193,7 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
|
|||
TEST_CHECK(exists(test_path / "temp_storage2/temp_storage"));
|
||||
pm->async_move_storage(test_path, bind(on_move_storage, _1, _2, test_path.string()));
|
||||
|
||||
test_sleep(2000);
|
||||
test_sleep(1000);
|
||||
ios.reset();
|
||||
ios.poll(ec);
|
||||
|
||||
|
@ -197,7 +206,7 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
|
|||
TEST_CHECK(!exists(test_path / "part0"));
|
||||
pm->async_rename_file(0, "part0", none);
|
||||
|
||||
test_sleep(2000);
|
||||
test_sleep(1000);
|
||||
ios.reset();
|
||||
ios.poll(ec);
|
||||
|
||||
|
@ -217,15 +226,20 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
|
|||
|
||||
pm->async_rename_file(0, "temp_storage/test1.tmp", none);
|
||||
test_sleep(1000);
|
||||
ios.reset();
|
||||
ios.poll(ec);
|
||||
|
||||
TEST_CHECK(!exists(test_path / "part0"));
|
||||
TEST_CHECK(exists(test_path / "temp_storage/test1.tmp"));
|
||||
|
||||
ios.run(ec);
|
||||
|
||||
io.join();
|
||||
}
|
||||
page_aligned_allocator::free(piece);
|
||||
}
|
||||
|
||||
void test_remove(path const& test_path)
|
||||
void test_remove(path const& test_path, bool unbuffered)
|
||||
{
|
||||
file_storage fs;
|
||||
fs.add_file("temp_storage/test1.tmp", 8);
|
||||
|
@ -241,9 +255,17 @@ void test_remove(path const& test_path)
|
|||
|
||||
boost::intrusive_ptr<torrent_info> info(new torrent_info(t.generate()));
|
||||
|
||||
session_settings set;
|
||||
set.disk_io_write_mode = set.disk_io_read_mode
|
||||
= unbuffered ? session_settings::disable_os_cache_for_aligned_files
|
||||
: session_settings::enable_os_cache;
|
||||
|
||||
file_pool fp;
|
||||
disk_buffer_pool dp(16 * 1024);
|
||||
boost::scoped_ptr<storage_interface> s(
|
||||
default_storage_constructor(fs, test_path, fp));
|
||||
s->m_settings = &set;
|
||||
s->m_disk_pool = &dp;
|
||||
|
||||
// allocate the files and create the directories
|
||||
s->initialize(true);
|
||||
|
@ -270,7 +292,8 @@ namespace
|
|||
}
|
||||
|
||||
void test_check_files(path const& test_path
|
||||
, libtorrent::storage_mode_t storage_mode)
|
||||
, libtorrent::storage_mode_t storage_mode
|
||||
, bool unbuffered)
|
||||
{
|
||||
boost::intrusive_ptr<torrent_info> info;
|
||||
|
||||
|
@ -335,7 +358,7 @@ void test_check_files(path const& test_path
|
|||
io.join();
|
||||
}
|
||||
|
||||
void run_test(path const& test_path)
|
||||
void run_test(path const& test_path, bool unbuffered)
|
||||
{
|
||||
std::cerr << "\n=== " << test_path.string() << " ===\n" << std::endl;
|
||||
|
||||
|
@ -348,26 +371,35 @@ void run_test(path const& test_path)
|
|||
fs.add_file("temp_storage/test2.tmp", 612);
|
||||
fs.add_file("temp_storage/test3.tmp", 0);
|
||||
fs.add_file("temp_storage/test4.tmp", 0);
|
||||
fs.add_file("temp_storage/test5.tmp", 1);
|
||||
fs.add_file("temp_storage/test5.tmp", 3253);
|
||||
fs.add_file("temp_storage/test6.tmp", 841);
|
||||
const int last_file_size = 4 * piece_size - fs.total_size();
|
||||
fs.add_file("temp_storage/test7.tmp", last_file_size);
|
||||
|
||||
libtorrent::create_torrent t(fs, piece_size, -1, 0);
|
||||
t.set_hash(0, hasher(piece0, piece_size).final());
|
||||
t.set_hash(1, hasher(piece1, piece_size).final());
|
||||
t.set_hash(2, hasher(piece2, piece_size).final());
|
||||
|
||||
|
||||
info = new torrent_info(t.generate());
|
||||
std::cerr << "=== test 1 ===" << std::endl;
|
||||
|
||||
run_storage_tests(info, fs, test_path, storage_mode_compact);
|
||||
run_storage_tests(info, fs, test_path, storage_mode_compact, unbuffered);
|
||||
|
||||
// make sure the files have the correct size
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test1.tmp") << std::endl;
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test1.tmp") == 17);
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test2.tmp") << std::endl;
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test2.tmp") == 31);
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test2.tmp") == 612);
|
||||
TEST_CHECK(exists(test_path / "temp_storage/test3.tmp"));
|
||||
TEST_CHECK(exists(test_path / "temp_storage/test4.tmp"));
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test5.tmp") == 3253);
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test6.tmp") == 841);
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test7.tmp") == last_file_size - piece_size);
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test1.tmp") << std::endl;
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test2.tmp") << std::endl;
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test3.tmp") << std::endl;
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test4.tmp") << std::endl;
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test5.tmp") << std::endl;
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test6.tmp") << std::endl;
|
||||
remove_all(test_path / "temp_storage");
|
||||
}
|
||||
|
||||
|
@ -375,7 +407,7 @@ void run_test(path const& test_path)
|
|||
|
||||
{
|
||||
file_storage fs;
|
||||
fs.add_file("temp_storage/test1.tmp", 17 + 612 + 1);
|
||||
fs.add_file("temp_storage/test1.tmp", 3 * piece_size);
|
||||
libtorrent::create_torrent t(fs, piece_size, -1, 0);
|
||||
TEST_CHECK(fs.begin()->path == "temp_storage/test1.tmp");
|
||||
t.set_hash(0, hasher(piece0, piece_size).final());
|
||||
|
@ -386,20 +418,19 @@ void run_test(path const& test_path)
|
|||
|
||||
std::cerr << "=== test 3 ===" << std::endl;
|
||||
|
||||
run_storage_tests(info, fs, test_path, storage_mode_compact);
|
||||
run_storage_tests(info, fs, test_path, storage_mode_compact, unbuffered);
|
||||
|
||||
// 48 = piece_size * 3
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test1.tmp") == 48);
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test1.tmp") == piece_size * 3);
|
||||
remove_all(test_path / "temp_storage");
|
||||
|
||||
// ==============================================
|
||||
|
||||
std::cerr << "=== test 4 ===" << std::endl;
|
||||
|
||||
run_storage_tests(info, fs, test_path, storage_mode_allocate);
|
||||
run_storage_tests(info, fs, test_path, storage_mode_allocate, unbuffered);
|
||||
|
||||
std::cerr << file_size(test_path / "temp_storage" / "test1.tmp") << std::endl;
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test1.tmp") == 17 + 612 + 1);
|
||||
TEST_CHECK(file_size(test_path / "temp_storage" / "test1.tmp") == 3 * piece_size);
|
||||
|
||||
remove_all(test_path / "temp_storage");
|
||||
|
||||
|
@ -408,24 +439,24 @@ void run_test(path const& test_path)
|
|||
// ==============================================
|
||||
|
||||
std::cerr << "=== test 5 ===" << std::endl;
|
||||
test_remove(test_path);
|
||||
test_remove(test_path, unbuffered);
|
||||
|
||||
// ==============================================
|
||||
|
||||
std::cerr << "=== test 6 ===" << std::endl;
|
||||
test_check_files(test_path, storage_mode_sparse);
|
||||
test_check_files(test_path, storage_mode_compact);
|
||||
test_check_files(test_path, storage_mode_sparse, unbuffered);
|
||||
test_check_files(test_path, storage_mode_compact, unbuffered);
|
||||
}
|
||||
|
||||
void test_fastresume()
|
||||
void test_fastresume(path const& test_path)
|
||||
{
|
||||
std::cout << "\n\n=== test fastresume ===" << std::endl;
|
||||
remove_all("tmp1");
|
||||
create_directory("tmp1");
|
||||
std::ofstream file("tmp1/temporary");
|
||||
remove_all(test_path / "tmp1");
|
||||
create_directory(test_path / "tmp1");
|
||||
std::ofstream file((test_path / "tmp1/temporary").external_file_string().c_str());
|
||||
boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file);
|
||||
file.close();
|
||||
TEST_CHECK(exists("tmp1/temporary"));
|
||||
TEST_CHECK(exists(test_path / "tmp1/temporary"));
|
||||
|
||||
entry resume;
|
||||
{
|
||||
|
@ -433,7 +464,7 @@ void test_fastresume()
|
|||
ses.set_alert_mask(alert::all_categories);
|
||||
|
||||
torrent_handle h = ses.add_torrent(boost::intrusive_ptr<torrent_info>(new torrent_info(*t))
|
||||
, "tmp1", entry()
|
||||
, test_path / "tmp1", entry()
|
||||
, storage_mode_compact);
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
|
@ -450,14 +481,14 @@ void test_fastresume()
|
|||
resume = h.write_resume_data();
|
||||
ses.remove_torrent(h, session::delete_files);
|
||||
}
|
||||
TEST_CHECK(!exists("tmp1/temporary"));
|
||||
TEST_CHECK(!exists(test_path / "tmp1/temporary"));
|
||||
resume.print(std::cout);
|
||||
|
||||
// make sure the fast resume check fails! since we removed the file
|
||||
{
|
||||
session ses(fingerprint(" ", 0,0,0,0), 0);
|
||||
ses.set_alert_mask(alert::all_categories);
|
||||
torrent_handle h = ses.add_torrent(t, "tmp1", resume
|
||||
torrent_handle h = ses.add_torrent(t, test_path / "tmp1", resume
|
||||
, storage_mode_compact);
|
||||
|
||||
std::auto_ptr<alert> a = ses.pop_alert();
|
||||
|
@ -475,7 +506,7 @@ void test_fastresume()
|
|||
}
|
||||
TEST_CHECK(dynamic_cast<fastresume_rejected_alert*>(a.get()) != 0);
|
||||
}
|
||||
remove_all("tmp1");
|
||||
remove_all(test_path / "tmp1");
|
||||
}
|
||||
|
||||
bool got_file_rename_alert(alert* a)
|
||||
|
@ -484,15 +515,15 @@ bool got_file_rename_alert(alert* a)
|
|||
|| dynamic_cast<libtorrent::file_rename_failed_alert*>(a);
|
||||
}
|
||||
|
||||
void test_rename_file_in_fastresume()
|
||||
void test_rename_file_in_fastresume(path const& test_path)
|
||||
{
|
||||
std::cout << "\n\n=== test rename file in fastresume ===" << std::endl;
|
||||
remove_all("tmp2");
|
||||
create_directory("tmp2");
|
||||
std::ofstream file("tmp2/temporary");
|
||||
remove_all(test_path / "tmp2");
|
||||
create_directory(test_path / "tmp2");
|
||||
std::ofstream file((test_path / "tmp2/temporary").external_file_string().c_str());
|
||||
boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file);
|
||||
file.close();
|
||||
TEST_CHECK(exists("tmp2/temporary"));
|
||||
TEST_CHECK(exists(test_path / "tmp2/temporary"));
|
||||
|
||||
entry resume;
|
||||
{
|
||||
|
@ -500,7 +531,7 @@ void test_rename_file_in_fastresume()
|
|||
ses.set_alert_mask(alert::all_categories);
|
||||
|
||||
torrent_handle h = ses.add_torrent(boost::intrusive_ptr<torrent_info>(new torrent_info(*t))
|
||||
, "tmp2", entry()
|
||||
, test_path / "tmp2", entry()
|
||||
, storage_mode_compact);
|
||||
|
||||
h.rename_file(0, "testing_renamed_files");
|
||||
|
@ -515,8 +546,8 @@ void test_rename_file_in_fastresume()
|
|||
resume = h.write_resume_data();
|
||||
ses.remove_torrent(h);
|
||||
}
|
||||
TEST_CHECK(!exists("tmp2/temporary"));
|
||||
TEST_CHECK(exists("tmp2/testing_renamed_files"));
|
||||
TEST_CHECK(!exists(test_path / "tmp2/temporary"));
|
||||
TEST_CHECK(exists(test_path / "tmp2/testing_renamed_files"));
|
||||
TEST_CHECK(resume.dict().find("mapped_files") != resume.dict().end());
|
||||
resume.print(std::cout);
|
||||
|
||||
|
@ -524,7 +555,7 @@ void test_rename_file_in_fastresume()
|
|||
{
|
||||
session ses(fingerprint(" ", 0,0,0,0), 0);
|
||||
ses.set_alert_mask(alert::all_categories);
|
||||
torrent_handle h = ses.add_torrent(t, "tmp2", resume
|
||||
torrent_handle h = ses.add_torrent(t, test_path / "tmp2", resume
|
||||
, storage_mode_compact);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
|
@ -535,13 +566,18 @@ void test_rename_file_in_fastresume()
|
|||
torrent_status stat = h.status();
|
||||
TEST_CHECK(stat.state == torrent_status::seeding);
|
||||
}
|
||||
remove_all("tmp2");
|
||||
remove_all(test_path / "tmp2");
|
||||
}
|
||||
|
||||
int test_main()
|
||||
{
|
||||
test_fastresume();
|
||||
test_rename_file_in_fastresume();
|
||||
// initialize test pieces
|
||||
for (char* p = piece0, *end(piece0 + piece_size); p < end; ++p)
|
||||
*p = rand();
|
||||
for (char* p = piece1, *end(piece1 + piece_size); p < end; ++p)
|
||||
*p = rand();
|
||||
for (char* p = piece2, *end(piece2 + piece_size); p < end; ++p)
|
||||
*p = rand();
|
||||
|
||||
std::vector<path> test_paths;
|
||||
char* env = std::getenv("TORRENT_TEST_PATHS");
|
||||
|
@ -559,7 +595,10 @@ int test_main()
|
|||
}
|
||||
}
|
||||
|
||||
std::for_each(test_paths.begin(), test_paths.end(), bind(&run_test, _1));
|
||||
std::for_each(test_paths.begin(), test_paths.end(), bind(&test_fastresume, _1));
|
||||
std::for_each(test_paths.begin(), test_paths.end(), bind(&test_rename_file_in_fastresume, _1));
|
||||
std::for_each(test_paths.begin(), test_paths.end(), bind(&run_test, _1, true));
|
||||
std::for_each(test_paths.begin(), test_paths.end(), bind(&run_test, _1, false));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ int test_main()
|
|||
|
||||
// calculate the hash for all pieces
|
||||
int num = t.num_pieces();
|
||||
std::vector<char> buf(t.piece_length());
|
||||
char* buf = page_aligned_allocator::malloc(t.piece_length());
|
||||
|
||||
file_pool fp;
|
||||
boost::scoped_ptr<storage_interface> s(default_storage_constructor(
|
||||
|
@ -166,8 +166,8 @@ int test_main()
|
|||
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
s->read(&buf[0], i, 0, fs.piece_size(i));
|
||||
hasher h(&buf[0], fs.piece_size(i));
|
||||
s->read(buf, i, 0, fs.piece_size(i));
|
||||
hasher h(buf, fs.piece_size(i));
|
||||
t.set_hash(i, h.final());
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,7 @@ int test_main()
|
|||
|
||||
stop_web_server(8000);
|
||||
remove_all("./test_torrent_dir");
|
||||
page_aligned_allocator::free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue