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:
Arvid Norberg 2009-01-21 07:31:49 +00:00
parent 9a9f08f70f
commit 00808473e7
11 changed files with 423 additions and 316 deletions

View File

@ -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;
};

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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)(

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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()

View File

@ -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;
}

View File

@ -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;
}