updated disk IO to support unbuffered files

This commit is contained in:
Arvid Norberg 2009-01-11 02:02:34 +00:00
parent 20a0593fa3
commit 7592ad4aee
14 changed files with 785 additions and 306 deletions

View File

@ -1,3 +1,4 @@
* added support for unbuffered I/O for aligned files
* added workaround for sparse file issue on Windows Vista * added workaround for sparse file issue on Windows Vista
* added new lt_trackers extension to exchange trackers between * added new lt_trackers extension to exchange trackers between
peers peers

View File

@ -3194,6 +3194,7 @@ that will be sent to the tracker. The user-agent is a good way to identify your
int cache_size; int cache_size;
int cache_expiry; int cache_expiry;
bool use_read_cache; bool use_read_cache;
bool disk_io_no_buffer;
std::pair<int, int> outgoing_ports; std::pair<int, int> outgoing_ports;
char peer_tos; char peer_tos;
@ -3424,6 +3425,15 @@ in the write cache, to when it's forcefully flushed to disk. Default is 60 secon
``use_read_cache``, is set to true (default), the disk cache is also used to ``use_read_cache``, is set to true (default), the disk cache is also used to
cache pieces read from disk. Blocks for writing pieces takes presedence. cache pieces read from disk. Blocks for writing pieces takes presedence.
``disk_io_no_buffer`` defaults to true. When set to true, files are preferred
to be opened in unbuffered mode. This helps the operating system from growing
its file cache indefinitely. Currently only files whose offset in the torrent
is page aligned are opened in unbuffered mode. A page is typically 4096 bytes
and since blocks in bittorrent are 16kB, any file that is aligned to a block
or piece will get the benefit of be opened in unbuffered mode. It is therefore
recommended to make the largest file in a torrent the first file (with offset 0)
or use pad files to align all files to piece boundries.
``outgoing_ports``, if set to something other than (0, 0) is a range of ports ``outgoing_ports``, if set to something other than (0, 0) is a range of ports
used to bind outgoing sockets to. This may be useful for users whose router used to bind outgoing sockets to. This may be useful for users whose router
allows them to assign QoS classes to traffic based on its local port. It is allows them to assign QoS classes to traffic based on its local port. It is
@ -4949,8 +4959,8 @@ this::
struct storage_interface struct storage_interface
{ {
virtual bool initialize(bool allocate_files) = 0; virtual bool initialize(bool allocate_files) = 0;
virtual int read(char* buf, int slot, int offset, int size) = 0; virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs) = 0;
virtual int write(const char* buf, int slot, int offset, int size) = 0; virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs) = 0;
virtual bool move_storage(fs::path save_path) = 0; virtual bool move_storage(fs::path save_path) = 0;
virtual bool verify_resume_data(lazy_entry const& rd, std::string& error) = 0; virtual bool verify_resume_data(lazy_entry const& rd, std::string& error) = 0;
virtual bool write_resume_data(entry& rd) const = 0; virtual bool write_resume_data(entry& rd) const = 0;
@ -4978,31 +4988,56 @@ it will also ``ftruncate`` all files to their target size.
Returning ``true`` indicates an error occurred. Returning ``true`` indicates an error occurred.
read()
readv()
------ ------
:: ::
int read(char* buf, int slot, int offset, int size) = 0; int readv(file::iovec_t const* buf, int slot, int offset, int num_bufs) = 0;
This function should read the data in the given slot and at the given offset This function should read the data in the given ``slot`` and at the given ``offset``.
and ``size`` number of bytes. The data is to be copied to ``buf``. It should read ``num_bufs`` buffers, where the size of each buffer is specified in the
buffer array ``bufs``. The file::iovec_t type has the following members::
struct iovec_t
{
void* iov_base;
size_t iov_len;
};
The return value is the number of bytes actually read. The return value is the number of bytes actually read.
Every buffer in ``bufs`` can be assumed to be page aligned and be of a page aligned size,
except for the last buffer of the torrent. The buffer can be assumed to fit a fully page
aligned number of bytes though.
write()
writev()
------- -------
:: ::
int write(const char* buf, int slot, int offset, int size) = 0; int write(const char* buf, int slot, int offset, int size) = 0;
This function should write the data in ``buf`` to the given slot (``slot``) at offset This function should write the data to the given ``slot`` and at the given ``offset``.
``offset`` in that slot. The buffer size is ``size``. It should write ``num_bufs`` buffers, where the size of each buffer is specified in the
buffer array ``bufs``. The file::iovec_t type has the following members::
struct iovec_t
{
void* iov_base;
size_t iov_len;
};
The return value is the number of bytes actually written. The return value is the number of bytes actually written.
Every buffer in ``bufs`` can be assumed to be page aligned and be of a page aligned size,
except for the last buffer of the torrent. The buffer can be assumed to fit a fully page
aligned number of bytes though.
This function should write the data in ``buf`` to the given slot (``slot``) at offset
``offset`` in that slot. The buffer size is ``size``.
move_storage() move_storage()
-------------- --------------

View File

@ -34,6 +34,8 @@ POSSIBILITY OF SUCH DAMAGE.
#define TORRENT_DISK_BUFFER_HOLDER_HPP_INCLUDED #define TORRENT_DISK_BUFFER_HOLDER_HPP_INCLUDED
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"
#include <algorithm>
namespace libtorrent namespace libtorrent
{ {
@ -45,10 +47,16 @@ namespace libtorrent
{ {
disk_buffer_holder(aux::session_impl& ses, char* buf); 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);
disk_buffer_holder(disk_io_thread& iothread, char* buf, int num_blocks);
~disk_buffer_holder(); ~disk_buffer_holder();
char* release(); char* release();
char* get() const { return m_buf; } char* get() const { return m_buf; }
void reset(char* buf = 0); void reset(char* buf = 0, int num_blocks = 1);
void swap(disk_buffer_holder& h)
{
TORRENT_ASSERT(&h.m_iothread == &m_iothread);
std::swap(h.m_buf, m_buf);
}
typedef char* (disk_buffer_holder::*unspecified_bool_type)(); typedef char* (disk_buffer_holder::*unspecified_bool_type)();
operator unspecified_bool_type() const operator unspecified_bool_type() const
@ -57,6 +65,7 @@ namespace libtorrent
private: private:
disk_io_thread& m_iothread; disk_io_thread& m_iothread;
char* m_buf; char* m_buf;
int m_num_blocks;
}; };
} }

View File

@ -53,6 +53,14 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
struct page_aligned_allocator
{
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
static char* malloc(const size_type bytes);
static void free(char* const block);
};
struct cached_piece_info struct cached_piece_info
{ {
@ -197,10 +205,16 @@ namespace libtorrent
char* allocate_buffer(); char* allocate_buffer();
void free_buffer(char* buf); void free_buffer(char* buf);
char* allocate_buffers(int blocks);
void free_buffers(char* buf, int blocks);
#ifdef TORRENT_DEBUG #ifdef TORRENT_DEBUG
void check_invariant() const; void check_invariant() const;
#endif #endif
int block_size() const { return m_block_size; }
bool no_buffer() const { return m_disk_io_no_buffer; }
private: private:
struct cached_piece_entry struct cached_piece_entry
@ -282,14 +296,16 @@ namespace libtorrent
// falls back to writing each block separately. // falls back to writing each block separately.
bool m_coalesce_writes; bool m_coalesce_writes;
bool m_coalesce_reads; bool m_coalesce_reads;
bool m_use_read_cache; bool m_use_read_cache;
bool m_disk_io_no_buffer;
// this only protects the pool allocator // this only protects the pool allocator
mutable mutex_t m_pool_mutex; mutable mutex_t m_pool_mutex;
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR #ifndef TORRENT_DISABLE_POOL_ALLOCATOR
// memory pool for read and write operations // memory pool for read and write operations
// and disk cache // and disk cache
boost::pool<> m_pool; boost::pool<page_aligned_allocator> m_pool;
#endif #endif
// number of bytes per block. The BitTorrent // number of bytes per block. The BitTorrent

View File

@ -59,6 +59,15 @@ POSSIBILITY OF SUCH DAMAGE.
#else #else
// posix part // posix part
#define _FILE_OFFSET_BITS 64 #define _FILE_OFFSET_BITS 64
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#include <unistd.h> #include <unistd.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <fcntl.h> #include <fcntl.h>
@ -75,18 +84,26 @@ namespace libtorrent
enum enum
{ {
// 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 #ifdef TORRENT_WINDOWS
read_only = GENERIC_READ, read_only = GENERIC_READ,
write_only = GENERIC_WRITE, write_only = GENERIC_WRITE,
read_write = GENERIC_READ | GENERIC_WRITE, read_write = GENERIC_READ | GENERIC_WRITE,
begin = FILE_BEGIN, rw_mask = GENERIC_READ | GENERIC_WRITE,
end = FILE_END, no_buffer = 1
#else #else
begin = SEEK_SET,
end = SEEK_END,
read_only = O_RDONLY, read_only = O_RDONLY,
write_only = O_WRONLY | O_CREAT, write_only = O_WRONLY | O_CREAT,
read_write = O_RDWR | O_CREAT, read_write = O_RDWR | O_CREAT,
rw_mask = O_RDONLY | O_WRONLY | O_RDWR | O_CREAT,
#if defined O_DIRECT
no_buffer = O_DIRECT
#else
no_buffer = O_SYNC
#endif
#endif #endif
}; };
@ -109,26 +126,28 @@ namespace libtorrent
void close(); void close();
bool set_size(size_type size, error_code& ec); bool set_size(size_type size, error_code& ec);
size_type writev(iovec_t const* bufs, int num_bufs, error_code& ec); size_type writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec);
size_type readv(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);
size_type write(char const*, size_type num_bytes, error_code& ec); size_type get_size(error_code& ec);
size_type read(char*, size_type num_bytes, error_code& ec);
size_type seek(size_type pos, int m, error_code& ec);
size_type tell(error_code& ec);
private: private:
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
HANDLE m_file_handle; HANDLE m_file_handle;
#ifdef TORRENT_USE_WPATH
std::wstring m_path;
#else
std::string m_path;
#endif
#else #else
int m_fd; int m_fd;
#endif #endif
#ifdef TORRENT_DEBUG #if defined TORRENT_WINDOWS || defined TORRENT_DEBUG
int m_open_mode; void init_file();
static int m_page_size;
#endif #endif
int m_open_mode;
}; };
} }

View File

@ -125,6 +125,7 @@ namespace libtorrent
, cache_size(512) , cache_size(512)
, cache_expiry(60) , cache_expiry(60)
, use_read_cache(true) , use_read_cache(true)
, disk_io_no_buffer(true)
, outgoing_ports(0,0) , outgoing_ports(0,0)
, peer_tos(0) , peer_tos(0)
, active_downloads(8) , active_downloads(8)
@ -377,6 +378,10 @@ namespace libtorrent
// cache for caching blocks read from disk too // cache for caching blocks read from disk too
bool use_read_cache; 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;
// if != (0, 0), this is the range of ports that // if != (0, 0), this is the range of ports that
// outgoing connections will be bound to. This // outgoing connections will be bound to. This
// is useful for users that have routers that // is useful for users that have routers that

View File

@ -57,8 +57,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/peer_request.hpp" #include "libtorrent/peer_request.hpp"
#include "libtorrent/hasher.hpp" #include "libtorrent/hasher.hpp"
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"
#include "libtorrent/buffer.hpp"
#include "libtorrent/file.hpp" #include "libtorrent/file.hpp"
#include "libtorrent/disk_buffer_holder.hpp"
namespace libtorrent namespace libtorrent
{ {
@ -72,7 +72,6 @@ namespace libtorrent
class session; class session;
struct file_pool; struct file_pool;
struct disk_io_job; struct disk_io_job;
struct disk_buffer_holder;
enum storage_mode_t enum storage_mode_t
{ {
@ -124,8 +123,8 @@ namespace libtorrent
// false return value indicates an error // false return value indicates an error
virtual bool initialize(bool allocate_files) = 0; virtual bool initialize(bool allocate_files) = 0;
virtual int readv(file::iovec_t* bufs, int slot, int offset, int num_bufs); virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs);
virtual int writev(file::iovec_t* buf, int slot, int offset, int num_bufs); virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs);
// negative return value indicates an error // negative return value indicates an error
virtual int read(char* buf, int slot, int offset, int size) = 0; virtual int read(char* buf, int slot, int offset, int size) = 0;
@ -168,6 +167,8 @@ namespace libtorrent
// non-zero return value indicates an error // non-zero return value indicates an error
virtual bool delete_files() = 0; virtual bool delete_files() = 0;
disk_io_thread& io_thread() { return *m_io_thread; }
void set_error(boost::filesystem::path const& file, error_code const& ec) const void set_error(boost::filesystem::path const& file, error_code const& ec) const
{ {
m_error_file = file.string(); m_error_file = file.string();
@ -182,6 +183,8 @@ namespace libtorrent
mutable std::string m_error_file; mutable std::string m_error_file;
virtual ~storage_interface() {} virtual ~storage_interface() {}
disk_io_thread* m_io_thread;
}; };
typedef storage_interface* (&storage_constructor_type)( typedef storage_interface* (&storage_constructor_type)(
@ -321,7 +324,7 @@ namespace libtorrent
// -1=error 0=ok 1=skip // -1=error 0=ok 1=skip
int check_one_piece(int& have_piece); int check_one_piece(int& have_piece);
int identify_data( int identify_data(
const std::vector<char>& piece_data char const* piece_data
, int current_slot); , int current_slot);
void switch_to_full_mode(); void switch_to_full_mode();
@ -394,8 +397,8 @@ namespace libtorrent
// used to move pieces while expanding // used to move pieces while expanding
// the storage from compact allocation // the storage from compact allocation
// to full allocation // to full allocation
buffer m_scratch_buffer; disk_buffer_holder m_scratch_buffer;
buffer m_scratch_buffer2; disk_buffer_holder m_scratch_buffer2;
// the piece that is in the scratch buffer // the piece that is in the scratch buffer
int m_scratch_piece; int m_scratch_piece;
@ -404,7 +407,7 @@ namespace libtorrent
storage_constructor_type m_storage_constructor; storage_constructor_type m_storage_constructor;
// temporary buffer used while checking // temporary buffer used while checking
std::vector<char> m_piece_data; disk_buffer_holder m_piece_data;
// this maps a piece hash to piece index. It will be // this maps a piece hash to piece index. It will be
// build the first time it is used (to save time if it // build the first time it is used (to save time if it

View File

@ -38,21 +38,32 @@ namespace libtorrent
{ {
disk_buffer_holder::disk_buffer_holder(aux::session_impl& ses, char* buf) disk_buffer_holder::disk_buffer_holder(aux::session_impl& ses, char* buf)
: m_iothread(ses.m_disk_thread), m_buf(buf) : m_iothread(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_iothread.is_disk_buffer(buf));
} }
disk_buffer_holder::disk_buffer_holder(disk_io_thread& iothread, char* buf) disk_buffer_holder::disk_buffer_holder(disk_io_thread& iothread, char* buf)
: m_iothread(iothread), m_buf(buf) : m_iothread(iothread), m_buf(buf), m_num_blocks(1)
{ {
TORRENT_ASSERT(buf == 0 || m_iothread.is_disk_buffer(buf)); TORRENT_ASSERT(buf == 0 || m_iothread.is_disk_buffer(buf));
} }
void disk_buffer_holder::reset(char* 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)
{ {
if (m_buf) m_iothread.free_buffer(m_buf); TORRENT_ASSERT(buf == 0 || m_iothread.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);
}
m_buf = buf; m_buf = buf;
m_num_blocks = num_blocks;
} }
char* disk_buffer_holder::release() char* disk_buffer_holder::release()
@ -64,7 +75,11 @@ namespace libtorrent
disk_buffer_holder::~disk_buffer_holder() disk_buffer_holder::~disk_buffer_holder()
{ {
if (m_buf) m_iothread.free_buffer(m_buf); if (m_buf)
{
if (m_num_blocks == 1) m_iothread.free_buffer(m_buf);
else m_iothread.free_buffers(m_buf, m_num_blocks);
}
} }
} }

View File

@ -34,13 +34,14 @@ POSSIBILITY OF SUCH DAMAGE.
#include <deque> #include <deque>
#include "libtorrent/disk_io_thread.hpp" #include "libtorrent/disk_io_thread.hpp"
#include "libtorrent/disk_buffer_holder.hpp" #include "libtorrent/disk_buffer_holder.hpp"
#include "libtorrent/alloca.hpp"
#include "libtorrent/invariant_check.hpp"
#include <boost/scoped_array.hpp> #include <boost/scoped_array.hpp>
#ifdef _WIN32 #ifdef TORRENT_WINDOWS
#include <malloc.h> #include <Windows.h>
#ifndef alloca #else
#define alloca(s) _alloca(s) #include <stdlib.h>
#endif
#endif #endif
#ifdef TORRENT_DISK_STATS #ifdef TORRENT_DISK_STATS
@ -50,23 +51,33 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
char* page_aligned_allocator::malloc(const size_type bytes)
{
#ifdef TORRENT_WINDOWS
return reinterpret_cast<char*>(VirtualAlloc(0, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
#else
return reinterpret_cast<char*>(valloc(bytes));
#endif
}
void page_aligned_allocator::free(char* const block)
{
#ifdef TORRENT_WINDOWS
VirtualFree(block, 0, MEM_RELEASE);
#else
std::free(block);
#endif
}
disk_io_thread::disk_io_thread(asio::io_service& ios, int block_size) disk_io_thread::disk_io_thread(asio::io_service& ios, int block_size)
: m_abort(false) : m_abort(false)
, m_queue_buffer_size(0) , m_queue_buffer_size(0)
, m_cache_size(512) // 512 * 16kB = 8MB , m_cache_size(512) // 512 * 16kB = 8MB
, m_cache_expiry(60) // 1 minute , m_cache_expiry(60) // 1 minute
// the file class doesn't support proper writev
// and readv on windows, so it's more efficient
// to coalesce reads and writes into a bigger
// buffer first
#ifdef TORRENT_WINDOWS
, m_coalesce_writes(true)
, m_coalesce_reads(true)
#else
, m_coalesce_writes(false) , m_coalesce_writes(false)
, m_coalesce_reads(false) , m_coalesce_reads(false)
#endif
, m_use_read_cache(true) , m_use_read_cache(true)
, m_disk_io_no_buffer(true)
#ifndef TORRENT_DISABLE_POOL_ALLOCATOR #ifndef TORRENT_DISABLE_POOL_ALLOCATOR
, m_pool(block_size) , m_pool(block_size)
#endif #endif
@ -330,11 +341,10 @@ namespace libtorrent
int offset = 0; int offset = 0;
boost::scoped_array<char> buf; boost::scoped_array<char> buf;
boost::scoped_array<file::iovec_t> iov; file::iovec_t* iov = 0;
int iov_counter = 0; int iov_counter = 0;
if (m_coalesce_writes) buf.reset(new (std::nothrow) char[piece_size]); if (m_coalesce_writes) buf.reset(new (std::nothrow) char[piece_size]);
// TOOD: replace with alloca else iov = TORRENT_ALLOCA(file::iovec_t, blocks_in_piece);
else iov.reset(new file::iovec_t[blocks_in_piece]);
for (int i = 0; i <= blocks_in_piece; ++i) for (int i = 0; i <= blocks_in_piece; ++i)
{ {
@ -346,8 +356,7 @@ namespace libtorrent
l.unlock(); l.unlock();
if (iov) if (iov)
{ {
TORRENT_ASSERT(iov); p.storage->write_impl(iov, p.piece, (std::min)(
p.storage->write_impl(iov.get(), p.piece, (std::min)(
i * m_block_size, piece_size) - buffer_size, iov_counter); i * m_block_size, piece_size) - buffer_size, iov_counter);
iov_counter = 0; iov_counter = 0;
} }
@ -762,7 +771,7 @@ namespace libtorrent
++m_allocations; ++m_allocations;
#endif #endif
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR #ifdef TORRENT_DISABLE_POOL_ALLOCATOR
return (char*)malloc(m_block_size); return page_aligned_allocator::malloc(m_block_sizes);
#else #else
return (char*)m_pool.ordered_malloc(); return (char*)m_pool.ordered_malloc();
#endif #endif
@ -776,12 +785,40 @@ namespace libtorrent
--m_allocations; --m_allocations;
#endif #endif
#ifdef TORRENT_DISABLE_POOL_ALLOCATOR #ifdef TORRENT_DISABLE_POOL_ALLOCATOR
free(buf); page_aligned_allocator::free(buf);
#else #else
m_pool.ordered_free(buf); m_pool.ordered_free(buf);
#endif #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) bool disk_io_thread::test_error(disk_io_job& j)
{ {
error_code const& ec = j.storage->error(); error_code const& ec = j.storage->error();
@ -870,6 +907,7 @@ namespace libtorrent
m_cache_size = s.cache_size; m_cache_size = s.cache_size;
m_cache_expiry = s.cache_expiry; m_cache_expiry = s.cache_expiry;
m_use_read_cache = s.use_read_cache; m_use_read_cache = s.use_read_cache;
m_disk_io_no_buffer = s.disk_io_no_buffer;
} }
case disk_io_job::abort_torrent: case disk_io_job::abort_torrent:
{ {

View File

@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/pch.hpp" #include "libtorrent/pch.hpp"
#include "libtorrent/config.hpp" #include "libtorrent/config.hpp"
#include "libtorrent/alloca.hpp"
#include <boost/scoped_ptr.hpp> #include <boost/scoped_ptr.hpp>
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
@ -70,6 +71,8 @@ BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);
#include "libtorrent/assert.hpp" #include "libtorrent/assert.hpp"
BOOST_STATIC_ASSERT((libtorrent::file::rw_mask & libtorrent::file::no_buffer) == 0);
namespace namespace
{ {
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
@ -107,9 +110,7 @@ namespace libtorrent
#else #else
: m_fd(-1) : m_fd(-1)
#endif #endif
#ifdef TORRENT_DEBUG
, m_open_mode(0) , m_open_mode(0)
#endif
{} {}
file::file(fs::path const& path, int mode, error_code& ec) file::file(fs::path const& path, int mode, error_code& ec)
@ -118,9 +119,7 @@ namespace libtorrent
#else #else
: m_fd(-1) : m_fd(-1)
#endif #endif
#ifdef TORRENT_DEBUG
, m_open_mode(0) , m_open_mode(0)
#endif
{ {
open(path, mode, ec); open(path, mode, ec);
} }
@ -136,18 +135,19 @@ namespace libtorrent
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
#ifdef TORRENT_USE_WPATH #ifdef TORRENT_USE_WPATH
std::wstring file_path(safe_convert(path.external_file_string())); m_path = safe_convert(path.external_file_string());
#else #else
std::string file_path = utf8_native(path.external_file_string()); m_path = utf8_native(path.external_file_string());
#endif #endif
m_file_handle = CreateFile( m_file_handle = CreateFile(
file_path.c_str() m_path.c_str()
, mode , mode & rw_mask
, FILE_SHARE_READ , FILE_SHARE_READ
, 0 , 0
, (mode == read_write || mode == write_only)?OPEN_ALWAYS:OPEN_EXISTING , ((mode & rw_mask) == read_write || (mode & rw_mask) == write_only)?OPEN_ALWAYS:OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL , FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS
| (mode & no_buffer?FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING:0)
, 0); , 0);
if (m_file_handle == INVALID_HANDLE_VALUE) if (m_file_handle == INVALID_HANDLE_VALUE)
@ -157,7 +157,7 @@ namespace libtorrent
} }
// try to make the file sparse if supported // try to make the file sparse if supported
if (mode == write_only || mode == read_write) if ((mode & rw_mask) == write_only || (mode & rw_mask) == read_write)
{ {
DWORD temp; DWORD temp;
::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0 ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0
@ -178,10 +178,23 @@ namespace libtorrent
ec = error_code(errno, get_posix_category()); ec = error_code(errno, get_posix_category());
return false; return false;
} }
#ifdef F_NOCACHE
if (mode & no_buffer)
{
int yes = 1;
fcntl(m_fd, F_NOCACHE, &yes);
}
#endif
#ifdef POSIX_FADV_RANDOM
// disable read-ahead
posix_fadvise(m_fd, 0, 0, POSIX_FADV_RANDOM);
#endif
#endif #endif
#ifdef TORRENT_DEBUG
m_open_mode = mode; m_open_mode = mode;
#endif
TORRENT_ASSERT(is_open()); TORRENT_ASSERT(is_open());
return true; return true;
} }
@ -201,119 +214,401 @@ namespace libtorrent
if (m_file_handle == INVALID_HANDLE_VALUE) return; if (m_file_handle == INVALID_HANDLE_VALUE) return;
CloseHandle(m_file_handle); CloseHandle(m_file_handle);
m_file_handle = INVALID_HANDLE_VALUE; m_file_handle = INVALID_HANDLE_VALUE;
m_path.clear();
#else #else
if (m_fd == -1) return; if (m_fd == -1) return;
::close(m_fd); ::close(m_fd);
m_fd = -1; m_fd = -1;
#endif #endif
#ifdef TORRENT_DEBUG
m_open_mode = 0; m_open_mode = 0;
#endif
} }
size_type file::read(char* buf, size_type num_bytes, error_code& ec) // defined in storage.cpp
int bufs_size(file::iovec_t const* bufs, int num_bufs);
#if defined TORRENT_WINDOWS || defined TORRENT_DEBUG
int file::m_page_size = 0;
void file::init_file()
{ {
TORRENT_ASSERT(m_open_mode == read_only || m_open_mode == read_write); if (m_page_size != 0) return;
TORRENT_ASSERT(buf);
TORRENT_ASSERT(num_bytes >= 0);
TORRENT_ASSERT(is_open());
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
TORRENT_ASSERT(DWORD(num_bytes) == num_bytes); SYSTEM_INFO si;
DWORD ret = 0; GetSystemInfo(&si);
if (num_bytes != 0) m_page_size = si.dwPageSize;
{
if (ReadFile(m_file_handle, buf, (DWORD)num_bytes, &ret, 0) == FALSE)
{
ec = error_code(GetLastError(), get_system_category());
return -1;
}
}
#else #else
size_type ret = ::read(m_fd, buf, num_bytes); m_page_size = sysconf(_SC_PAGESIZE);
if (ret == -1) ec = error_code(errno, get_posix_category());
#endif #endif
return ret;
} }
size_type file::readv(iovec_t const* bufs, int num_bufs, error_code& ec) #endif
size_type file::readv(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec)
{ {
TORRENT_ASSERT(m_open_mode == read_only || m_open_mode == read_write); TORRENT_ASSERT((m_open_mode & rw_mask) == read_only || (m_open_mode & rw_mask) == read_write);
TORRENT_ASSERT(bufs); TORRENT_ASSERT(bufs);
TORRENT_ASSERT(num_bufs >= 0); TORRENT_ASSERT(num_bufs > 0);
TORRENT_ASSERT(is_open()); TORRENT_ASSERT(is_open());
#ifdef TORRENT_WINDOWS #if defined TORRENT_WINDOWS || defined TORRENT_DEBUG
// TODO: Replace with ReadFileScatter if possible // make sure m_page_size is initialized
size_type ret = 0; init_file();
for (iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
{
if (i->iov_len <= 0) continue;
DWORD intermediate = 0;
if (ReadFile(m_file_handle, i->iov_base, (DWORD)i->iov_len, &intermediate, 0) == FALSE)
{
ec = error_code(GetLastError(), get_system_category());
return -1;
}
ret += intermediate;
}
#else
size_type ret = ::readv(m_fd, bufs, num_bufs);
if (ret == -1) ec = error_code(errno, get_posix_category());
#endif #endif
return ret;
}
size_type file::writev(iovec_t const* bufs, int num_bufs, error_code& ec) #ifdef TORRENT_DEBUG
{ if (m_open_mode & no_buffer)
TORRENT_ASSERT(m_open_mode == write_only || m_open_mode == read_write);
TORRENT_ASSERT(bufs);
TORRENT_ASSERT(num_bufs >= 0);
TORRENT_ASSERT(is_open());
#ifdef TORRENT_WINDOWS
// Replace by WriteFileGather if possible
size_type ret = 0;
for (iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
{ {
if (i->iov_len <= 0) continue; bool eof = false;
DWORD intermediate = 0; int size = 0;
if (WriteFile(m_file_handle, i->iov_base, (DWORD)i->iov_len, &intermediate, 0) == FALSE) TORRENT_ASSERT((file_offset & (m_page_size-1)) == 0);
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
{ {
ec = error_code(GetLastError(), get_system_category()); TORRENT_ASSERT((int(i->iov_base) & (m_page_size-1)) == 0);
return -1; // every buffer must be a multiple of the page size
// except for the last one
TORRENT_ASSERT((i->iov_len & (m_page_size-1)) == 0 || i == end-1);
if ((i->iov_len & (m_page_size-1)) != 0) eof = true;
size += i->iov_len;
} }
ret += intermediate; error_code code;
if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code));
} }
#else
size_type ret = ::writev(m_fd, bufs, num_bufs);
if (ret == -1) ec = error_code(errno, get_posix_category());
#endif #endif
return ret;
}
size_type file::write(const char* buf, size_type num_bytes, error_code& ec)
{
TORRENT_ASSERT(m_open_mode == write_only || m_open_mode == read_write);
TORRENT_ASSERT(buf);
TORRENT_ASSERT(num_bytes >= 0);
TORRENT_ASSERT(is_open());
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
DWORD ret = 0; DWORD ret = 0;
if (num_bytes != 0)
// since the ReadFileScatter requires the file to be opened
// with no buffering, and no buffering requires page aligned
// buffers, open the file in non-buffered mode in case the
// buffer is not aligned. Most of the times the buffer should
// be aligned though
if ((m_open_mode & no_buffer) == 0)
{ {
if (WriteFile(m_file_handle, buf, (DWORD)num_bytes, &ret, 0) == FALSE) // this means the buffer base or the buffer size is not aligned
// to the page size. Use a regular file for this operation.
LARGE_INTEGER offs;
offs.QuadPart = file_offset;
if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE)
{ {
ec = error_code(GetLastError(), get_system_category()); ec = error_code(GetLastError(), get_system_category());
return -1; return -1;
} }
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
{
DWORD intermediate = 0;
if (ReadFile(m_file_handle, (char*)bufs->iov_base
, (DWORD)bufs->iov_len, &intermediate, 0) == FALSE)
{
ec = error_code(GetLastError(), get_system_category());
return -1;
}
ret += intermediate;
}
return ret;
}
int size = bufs_size(bufs, num_bufs);
// number of pages for the read. round up
int num_pages = (size + m_page_size - 1) / m_page_size;
// allocate array of FILE_SEGMENT_ELEMENT for ReadFileScatter
FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1);
FILE_SEGMENT_ELEMENT* cur_seg = segment_array;
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
{
for (int k = 0; k < i->iov_len; k += m_page_size)
{
cur_seg->Buffer = ((char*)i->iov_base) + k;
++cur_seg;
}
}
// terminate the array
cur_seg->Buffer = 0;
OVERLAPPED ol;
ol.Internal = 0;
ol.InternalHigh = 0;
ol.OffsetHigh = file_offset >> 32;
ol.Offset = file_offset & 0xffffffff;
ol.Pointer = 0;
ol.hEvent = CreateEvent(0, true, false, 0);
ret += size;
size = num_pages * m_page_size;
if (ReadFileScatter(m_file_handle, segment_array, size, 0, &ol) == 0)
{
DWORD last_error = GetLastError();
if (last_error != ERROR_IO_PENDING && last_error != ERROR_HANDLE_EOF)
{
ec = error_code(GetLastError(), get_system_category());
CloseHandle(ol.hEvent);
return -1;
}
if (GetOverlappedResult(m_file_handle, &ol, &ret, true) == 0)
{
ec = error_code(GetLastError(), get_system_category());
CloseHandle(ol.hEvent);
return -1;
}
} }
#else
size_type ret = ::write(m_fd, buf, num_bytes);
if (ret == -1) ec = error_code(errno, get_posix_category());
#endif
return ret; return ret;
#else
size_type ret = lseek(m_fd, file_offset, SEEK_SET);
if (ret < 0)
{
ec = error_code(errno, get_posix_category());
return -1;
}
#ifdef TORRENT_LINUX
bool aligned = false;
int size = 0;
// if we're not opened in no-buffer mode, we don't need alignment
if ((m_open_mode & no_buffer) == 0) aligned = true;
if (!aligned)
{
size = bufs_size(bufs, num_bufs);
if (size & (m_page_size-1) == 0) aligned = true;
}
if (aligned)
#endif
{
ret = ::readv(m_fd, bufs, num_bufs);
if (ret < 0)
{
ec = error_code(errno, get_posix_category());
return -1;
}
return ret;
}
#ifdef TORRENT_LINUX
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 & ~(m_page_size-1)) + m_page_size;
ret = ::readv(m_fd, temp_bufs, num_bufs);
if (ret < 0)
{
ec = error_code(errno, get_posix_category());
return -1;
}
return (std::min)(ret, size_type(size));
#endif
#endif
}
size_type file::writev(size_type file_offset, iovec_t const* bufs, int num_bufs, error_code& ec)
{
TORRENT_ASSERT((m_open_mode & rw_mask) == write_only || (m_open_mode & rw_mask) == read_write);
TORRENT_ASSERT(bufs);
TORRENT_ASSERT(num_bufs > 0);
TORRENT_ASSERT(is_open());
#if defined TORRENT_WINDOWS || defined TORRENT_DEBUG
// make sure m_page_size is initialized
init_file();
#endif
#ifdef TORRENT_DEBUG
if (m_open_mode & no_buffer)
{
bool eof = false;
int size = 0;
TORRENT_ASSERT((file_offset & (m_page_size-1)) == 0);
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
{
TORRENT_ASSERT((int(i->iov_base) & (m_page_size-1)) == 0);
// every buffer must be a multiple of the page size
// except for the last one
TORRENT_ASSERT((i->iov_len & (m_page_size-1)) == 0 || i == end-1);
if ((i->iov_len & (m_page_size-1)) != 0) eof = true;
size += i->iov_len;
}
error_code code;
if (eof) TORRENT_ASSERT(file_offset + size >= get_size(code));
}
#endif
#ifdef TORRENT_WINDOWS
DWORD ret = 0;
// since the ReadFileScatter requires the file to be opened
// with no buffering, and no buffering requires page aligned
// buffers, open the file in non-buffered mode in case the
// buffer is not aligned. Most of the times the buffer should
// be aligned though
if ((m_open_mode & no_buffer) == 0)
{
// this means the buffer base or the buffer size is not aligned
// to the page size. Use a regular file for this operation.
LARGE_INTEGER offs;
offs.QuadPart = file_offset;
if (SetFilePointerEx(m_file_handle, offs, &offs, SEEK_SET) == FALSE)
{
ec = error_code(GetLastError(), get_system_category());
return -1;
}
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
{
DWORD intermediate = 0;
if (WriteFile(m_file_handle, (char const*)bufs->iov_base
, (DWORD)bufs->iov_len, &intermediate, 0) == FALSE)
{
ec = error_code(GetLastError(), get_system_category());
return -1;
}
ret += intermediate;
}
return ret;
}
int size = bufs_size(bufs, num_bufs);
// number of pages for the write. round up
int num_pages = (size + m_page_size - 1) / m_page_size;
// allocate array of FILE_SEGMENT_ELEMENT for WriteFileGather
FILE_SEGMENT_ELEMENT* segment_array = TORRENT_ALLOCA(FILE_SEGMENT_ELEMENT, num_pages + 1);
FILE_SEGMENT_ELEMENT* cur_seg = segment_array;
for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
{
for (int k = 0; k < i->iov_len; k += m_page_size)
{
cur_seg->Buffer = ((char*)i->iov_base) + k;
++cur_seg;
}
}
// terminate the array
cur_seg->Buffer = 0;
OVERLAPPED ol;
ol.Internal = 0;
ol.InternalHigh = 0;
ol.OffsetHigh = file_offset >> 32;
ol.Offset = file_offset & 0xffffffff;
ol.Pointer = 0;
ol.hEvent = CreateEvent(0, true, false, 0);
ret += size;
// if file_size is > 0, the file will be opened in unbuffered
// mode after the write completes, and truncate the file to
// file_size.
size_type file_size = 0;
if (size & (m_page_size-1) != 0)
{
// if size is not an even multiple, this must be the tail
// of the file. Write the whole page and then open a new
// file without FILE_FLAG_NO_BUFFERING and set the
// file size to file_offset + size
file_size = file_offset + size;
size = num_pages * m_page_size;
}
if (WriteFileGather(m_file_handle, segment_array, size, 0, &ol) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
ec = error_code(GetLastError(), get_system_category());
CloseHandle(ol.hEvent);
return -1;
}
if (GetOverlappedResult(m_file_handle, &ol, &ret, true) == 0)
{
ec = error_code(GetLastError(), get_system_category());
CloseHandle(ol.hEvent);
return -1;
}
}
CloseHandle(ol.hEvent);
if (file_size > 0)
{
HANDLE f = CreateFile(m_path.c_str(), GENERIC_WRITE
, FILE_SHARE_WRITE, 0, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0);
if (f == INVALID_HANDLE_VALUE)
{
ec = error_code(GetLastError(), get_system_category());
return false;
}
LARGE_INTEGER offs;
offs.QuadPart = file_size;
if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE)
{
CloseHandle(f);
ec = error_code(GetLastError(), get_system_category());
return -1;
}
if (::SetEndOfFile(f) == FALSE)
{
ec = error_code(GetLastError(), get_system_category());
CloseHandle(f);
return false;
}
CloseHandle(f);
}
return ret;
#else
size_type ret = lseek(m_fd, file_offset, SEEK_SET);
if (ret < 0)
{
ec = error_code(errno, get_posix_category());
return -1;
}
#ifdef TORRENT_LINUX
bool aligned = false;
int size = 0;
// if we're not opened in no-buffer mode, we don't need alignment
if ((m_open_mode & no_buffer) == 0) aligned = true;
if (!aligned)
{
size = bufs_size(bufs, num_bufs);
if (size & (m_page_size-1) == 0) aligned = true;
}
if (aligned)
#endif
{
ret = ::writev(m_fd, bufs, num_bufs);
if (ret < 0)
{
ec = error_code(errno, get_posix_category());
return -1;
}
return ret;
}
#ifdef TORRENT_LINUX
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 & ~(m_page_size-1)) + m_page_size;
ret = ::writev(m_fd, temp_bufs, num_bufs);
if (ret < 0)
{
ec = error_code(errno, get_posix_category());
return -1;
}
if (ftruncate(m_fd, file_offset + size) < 0)
{
ec = error_code(errno, get_posix_category());
return false;
}
return (std::min)(ret, size_type(size));
#endif
#endif
} }
bool file::set_size(size_type s, error_code& ec) bool file::set_size(size_type s, error_code& ec)
@ -322,10 +617,13 @@ namespace libtorrent
TORRENT_ASSERT(s >= 0); TORRENT_ASSERT(s >= 0);
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
size_type pos = tell(ec); LARGE_INTEGER offs;
if (ec) return false; offs.QuadPart = s;
seek(s, begin, ec); if (SetFilePointerEx(m_file_handle, offs, &offs, FILE_BEGIN) == FALSE)
if (ec) return false; {
ec = error_code(GetLastError(), get_system_category());
return false;
}
if (::SetEndOfFile(m_file_handle) == FALSE) if (::SetEndOfFile(m_file_handle) == FALSE)
{ {
ec = error_code(GetLastError(), get_system_category()); ec = error_code(GetLastError(), get_system_category());
@ -341,48 +639,24 @@ namespace libtorrent
return true; return true;
} }
size_type file::seek(size_type offset, int m, error_code& ec) size_type file::get_size(error_code& ec)
{ {
TORRENT_ASSERT(is_open());
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
LARGE_INTEGER offs; LARGE_INTEGER file_size;
offs.QuadPart = offset; if (!GetFileSizeEx(m_file_handle, &file_size))
if (SetFilePointerEx(m_file_handle, offs, &offs, m) == FALSE)
{ {
ec = error_code(GetLastError(), get_system_category()); ec = error_code(GetLastError(), get_system_category());
return -1; return -1;
} }
return offs.QuadPart; return file_size.QuadPart;
#else #else
size_type ret = lseek(m_fd, offset, m); struct stat fs;
if (ret < 0) ec = error_code(errno, get_posix_category()); if (fstat(m_fd, &fs) != 0)
return ret;
#endif
}
size_type file::tell(error_code& ec)
{
TORRENT_ASSERT(is_open());
#ifdef TORRENT_WINDOWS
LARGE_INTEGER offs;
offs.QuadPart = 0;
// is there any other way to get offset?
if (SetFilePointerEx(m_file_handle, offs, &offs
, FILE_CURRENT) == FALSE)
{ {
ec = error_code(GetLastError(), get_system_category()); ec = error_code(errno, get_posix_category());
return -1; return -1;
} }
return fs.st_size;
return offs.QuadPart;
#else
size_type ret;
ret = lseek(m_fd, 0, SEEK_CUR);
if (ret < 0) ec = error_code(errno, get_posix_category());
return ret;
#endif #endif
} }
} }

View File

@ -47,7 +47,8 @@ namespace libtorrent
{ {
TORRENT_ASSERT(st != 0); TORRENT_ASSERT(st != 0);
TORRENT_ASSERT(p.is_complete()); TORRENT_ASSERT(p.is_complete());
TORRENT_ASSERT(m == file::read_only || m == file::read_write); TORRENT_ASSERT((m & file::rw_mask) == file::read_only
|| (m & file::rw_mask) == file::read_write);
boost::mutex::scoped_lock l(m_mutex); boost::mutex::scoped_lock l(m_mutex);
typedef nth_index<file_set, 0>::type path_view; typedef nth_index<file_set, 0>::type path_view;
path_view& pt = get<0>(m_files); path_view& pt = get<0>(m_files);
@ -71,8 +72,8 @@ namespace libtorrent
// if we asked for a file in write mode, // if we asked for a file in write mode,
// and the cached file is is not opened in // and the cached file is is not opened in
// write mode, re-open it // write mode, re-open it
if ((e.mode != file::read_write) if (((e.mode & file::rw_mask) != file::read_write)
&& (m == file::read_write)) && ((m & file::rw_mask) == file::read_write))
{ {
// close the file before we open it with // close the file before we open it with
// the new read/write privilages // the new read/write privilages
@ -88,6 +89,7 @@ namespace libtorrent
e.mode = m; e.mode = m;
} }
pt.replace(i, e); pt.replace(i, e);
TORRENT_ASSERT((e.mode & ~file::rw_mask) == (m & ~file::rw_mask));
return e.file_ptr; return e.file_ptr;
} }
// the file is not in our cache // the file is not in our cache

View File

@ -844,7 +844,7 @@ namespace libtorrent { namespace dht
} }
#ifdef TORRENT_DHT_VERBOSE_LOGGING #ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_LOG(dht_tracker) << log_line.str() << " ]"; TORRENT_LOG(dht_tracker) << log_line.str() << " ]";
TORRENT_LOG(dht_tracker) << "ERROR: incoming error: " << std::dec << m.error_code TORRENT_LOG(dht_tracker) << "ERROR: incoming error: " << m.error_code
<< " " << m.error_msg; << " " << m.error_msg;
#endif #endif
return; return;

View File

@ -317,7 +317,7 @@ namespace libtorrent
// for backwards compatibility, let the default readv and // for backwards compatibility, let the default readv and
// writev implementations be implemented in terms of the // writev implementations be implemented in terms of the
// old read and write // old read and write
int storage_interface::readv(file::iovec_t* bufs int storage_interface::readv(file::iovec_t const* bufs
, int slot, int offset, int num_bufs) , int slot, int offset, int num_bufs)
{ {
int ret = 0; int ret = 0;
@ -331,7 +331,7 @@ namespace libtorrent
return ret; return ret;
} }
int storage_interface::writev(file::iovec_t* bufs, int slot int storage_interface::writev(file::iovec_t const* bufs, int slot
, int offset, int num_bufs) , int offset, int num_bufs)
{ {
int ret = 0; int ret = 0;
@ -380,17 +380,17 @@ namespace libtorrent
} }
} }
int bufs_size(file::iovec_t *bufs, int num_bufs) int bufs_size(file::iovec_t const* bufs, int num_bufs)
{ {
int size = 0; int size = 0;
for (file::iovec_t* i = bufs, *end(bufs + num_bufs); i < end; ++i) for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
size += i->iov_len; size += i->iov_len;
return size; return size;
} }
void clear_bufs(file::iovec_t *bufs, int num_bufs) void clear_bufs(file::iovec_t const* bufs, int num_bufs)
{ {
for (file::iovec_t* i = bufs, *end(bufs + num_bufs); i < end; ++i) for (file::iovec_t const* i = bufs, *end(bufs + num_bufs); i < end; ++i)
std::memset(i->iov_base, 0, i->iov_len); std::memset(i->iov_base, 0, i->iov_len);
} }
@ -400,10 +400,18 @@ namespace libtorrent
storage(file_storage const& fs, fs::path const& path, file_pool& fp) storage(file_storage const& fs, fs::path const& path, file_pool& fp)
: m_files(fs) : m_files(fs)
, m_pool(fp) , m_pool(fp)
, m_page_size(4096)
{ {
TORRENT_ASSERT(m_files.begin() != m_files.end()); TORRENT_ASSERT(m_files.begin() != m_files.end());
m_save_path = fs::complete(path); m_save_path = fs::complete(path);
TORRENT_ASSERT(m_save_path.is_complete()); TORRENT_ASSERT(m_save_path.is_complete());
#ifdef TORRENT_WINDOWS
SYSTEM_INFO si;
GetSystemInfo(&si);
m_page_size = si.dwPageSize;
#else
m_page_size = sysconf(_SC_PAGESIZE);
#endif
} }
bool rename_file(int index, std::string const& new_filename); bool rename_file(int index, std::string const& new_filename);
@ -413,8 +421,8 @@ namespace libtorrent
bool move_storage(fs::path save_path); bool move_storage(fs::path save_path);
int read(char* buf, int slot, int offset, int size); int read(char* buf, int slot, int offset, int size);
int write(char const* buf, int slot, int offset, int size); int write(char const* buf, int slot, int offset, int size);
int readv(file::iovec_t* bufs, int slot, int offset, int num_bufs); int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs);
int writev(file::iovec_t* buf, int slot, int offset, int num_bufs); int writev(file::iovec_t const* buf, int slot, int offset, int num_bufs);
bool move_slot(int src_slot, int dst_slot); bool move_slot(int src_slot, int dst_slot);
bool swap_slots(int slot1, int slot2); bool swap_slots(int slot1, int slot2);
bool swap_slots3(int slot1, int slot2, int slot3); bool swap_slots3(int slot1, int slot2, int slot3);
@ -436,42 +444,35 @@ namespace libtorrent
// the session, to make all storage // the session, to make all storage
// instances use the same pool // instances use the same pool
file_pool& m_pool; file_pool& m_pool;
// temporary storage for moving pieces int m_page_size;
buffer m_scratch_buffer;
}; };
sha1_hash storage::hash_for_slot(int slot, partial_hash& ph, int piece_size) sha1_hash storage::hash_for_slot(int slot, partial_hash& ph, int piece_size)
{ {
TORRENT_ASSERT(!error()); TORRENT_ASSERT(!error());
#ifdef TORRENT_DEBUG
hasher partial;
hasher whole;
int slot_size1 = piece_size;
m_scratch_buffer.resize(slot_size1);
read(&m_scratch_buffer[0], slot, 0, slot_size1);
if (error()) return sha1_hash(0);
if (ph.offset > 0)
partial.update(&m_scratch_buffer[0], ph.offset);
whole.update(&m_scratch_buffer[0], slot_size1);
hasher partial_copy = ph.h;
TORRENT_ASSERT(ph.offset == 0 || partial_copy.final() == partial.final());
#endif
int slot_size = piece_size - ph.offset; int slot_size = piece_size - ph.offset;
if (slot_size > 0) if (slot_size > 0)
{ {
m_scratch_buffer.resize(slot_size); int size = slot_size;
read(&m_scratch_buffer[0], slot, ph.offset, slot_size); int num_blocks = (size + io_thread().block_size() - 1) / io_thread().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_len = (std::min)(io_thread().block_size(), size);
size -= bufs[i].iov_len;
}
readv(bufs, slot, ph.offset, num_blocks);
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);
}
if (error()) return sha1_hash(0); if (error()) return sha1_hash(0);
ph.h.update(&m_scratch_buffer[0], slot_size);
} }
#ifdef TORRENT_DEBUG
sha1_hash ret = ph.h.final();
TORRENT_ASSERT(ret == whole.final());
return ret;
#else
return ph.h.final(); return ph.h.final();
#endif
} }
bool storage::initialize(bool allocate_files) bool storage::initialize(bool allocate_files)
@ -524,8 +525,12 @@ namespace libtorrent
|| (exists(file_path) && file_size(file_path) > file_iter->size)) || (exists(file_path) && file_size(file_path) > file_iter->size))
{ {
error_code ec; error_code ec;
int mode = file::read_write;
if (io_thread().no_buffer()
&& ((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 boost::shared_ptr<file> f = m_pool.open_file(this
, m_save_path / file_iter->path, file::read_write, ec); , m_save_path / file_iter->path, mode, ec);
if (ec) set_error(m_save_path / file_iter->path, ec); if (ec) set_error(m_save_path / file_iter->path, ec);
else if (f) else if (f)
{ {
@ -605,7 +610,6 @@ namespace libtorrent
bool storage::release_files() bool storage::release_files()
{ {
m_pool.release(this); m_pool.release(this);
buffer().swap(m_scratch_buffer);
return false; return false;
} }
@ -613,7 +617,6 @@ namespace libtorrent
{ {
// make sure we don't have the files open // make sure we don't have the files open
m_pool.release(this); m_pool.release(this);
buffer().swap(m_scratch_buffer);
int error = 0; int error = 0;
std::string error_file; std::string error_file;
@ -911,61 +914,97 @@ namespace libtorrent
*/ */
#endif #endif
#define TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size) \
int num_blocks = (piece_size + io_thread().block_size() - 1) / io_thread().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); \
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);
#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)
bool storage::move_slot(int src_slot, int dst_slot) bool storage::move_slot(int src_slot, int dst_slot)
{ {
bool r = true;
int piece_size = m_files.piece_size(dst_slot); int piece_size = m_files.piece_size(dst_slot);
m_scratch_buffer.resize(piece_size);
int ret = read(&m_scratch_buffer[0], src_slot, 0, piece_size); TORRENT_ALLOCATE_BLOCKS(bufs, num_blocks, piece_size);
if (ret != piece_size) return true;
ret = write(&m_scratch_buffer[0], dst_slot, 0, piece_size); readv(bufs, src_slot, 0, num_blocks); if (error()) goto ret;
if (ret != piece_size) return true; writev(bufs, dst_slot, 0, num_blocks); if (error()) goto ret;
return false;
r = false;
ret:
TORRENT_FREE_BLOCKS(bufs, num_blocks)
return r;
} }
bool storage::swap_slots(int slot1, int slot2) bool storage::swap_slots(int slot1, int slot2)
{ {
bool r = true;
// the size of the target slot is the size of the piece // the size of the target slot is the size of the piece
int piece_size = m_files.piece_length(); int piece_size = m_files.piece_length();
int piece1_size = m_files.piece_size(slot2); int piece1_size = m_files.piece_size(slot2);
int piece2_size = m_files.piece_size(slot1); int piece2_size = m_files.piece_size(slot1);
m_scratch_buffer.resize(piece_size * 2);
int ret = read(&m_scratch_buffer[0], slot1, 0, piece1_size); TORRENT_ALLOCATE_BLOCKS(bufs1, num_blocks1, piece1_size);
if (ret != piece1_size) return true; TORRENT_ALLOCATE_BLOCKS(bufs2, num_blocks2, piece2_size);
ret = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
if (ret != piece2_size) return true; readv(bufs1, slot1, 0, num_blocks1); if (error()) goto ret;
ret = write(&m_scratch_buffer[0], slot2, 0, piece1_size); readv(bufs2, slot2, 0, num_blocks2); if (error()) goto ret;
if (ret != piece1_size) return true; writev(bufs1, slot2, 0, num_blocks1); if (error()) goto ret;
ret = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size); writev(bufs2, slot1, 0, num_blocks2); if (error()) goto ret;
if (ret != piece2_size) return true;
return false; r = false;
ret:
TORRENT_FREE_BLOCKS(bufs1, num_blocks1)
TORRENT_FREE_BLOCKS(bufs2, num_blocks2)
return r;
} }
bool storage::swap_slots3(int slot1, int slot2, int slot3) bool storage::swap_slots3(int slot1, int slot2, int slot3)
{ {
bool r = true;
// the size of the target slot is the size of the piece // the size of the target slot is the size of the piece
int piece_size = m_files.piece_length(); int piece_size = m_files.piece_length();
int piece1_size = m_files.piece_size(slot2); int piece1_size = m_files.piece_size(slot2);
int piece2_size = m_files.piece_size(slot3); int piece2_size = m_files.piece_size(slot3);
int piece3_size = m_files.piece_size(slot1); int piece3_size = m_files.piece_size(slot1);
m_scratch_buffer.resize(piece_size * 2);
int ret = 0; TORRENT_ALLOCATE_BLOCKS(bufs1, num_blocks1, piece_size);
ret = read(&m_scratch_buffer[0], slot1, 0, piece1_size); TORRENT_ALLOCATE_BLOCKS(bufs2, num_blocks2, piece_size);
if (ret != piece1_size) return true;
ret = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size); int tmp1 = 0;
if (ret != piece2_size) return true; int tmp2 = 0;
ret = write(&m_scratch_buffer[0], slot2, 0, piece1_size); TORRENT_SET_SIZE(bufs1, piece1_size, tmp1);
if (ret != piece1_size) return true; readv(bufs1, slot1, 0, tmp1); if (error()) goto ret;
ret = read(&m_scratch_buffer[0], slot3, 0, piece3_size); TORRENT_SET_SIZE(bufs2, piece2_size, tmp2);
if (ret != piece3_size) return true; readv(bufs2, slot2, 0, tmp2); if (error()) goto ret;
ret = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size); writev(bufs1, slot2, 0, tmp1); if (error()) goto ret;
if (ret != piece2_size) return true; TORRENT_SET_SIZE(bufs1, piece3_size, tmp1);
ret = write(&m_scratch_buffer[0], slot1, 0, piece3_size); readv(bufs1, slot3, 0, tmp1); if (error()) goto ret;
if (ret != piece3_size) return true; writev(bufs2, slot3, 0, tmp2); if (error()) goto ret;
return false; writev(bufs1, slot1, 0, tmp1); if (error()) goto ret;
ret:
TORRENT_FREE_BLOCKS(bufs1, num_blocks1)
TORRENT_FREE_BLOCKS(bufs2, num_blocks2)
return r;
} }
int storage::readv( int storage::readv(
file::iovec_t* bufs file::iovec_t const* bufs
, int slot , int slot
, int offset , int offset
, int num_bufs) , int num_bufs)
@ -1022,7 +1061,9 @@ namespace libtorrent
int counter = 0; int counter = 0;
#endif #endif
file::iovec_t* current_buf = bufs; file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
file::iovec_t* current_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs);
copy_bufs(bufs, size, current_buf);
int read_bytes; int read_bytes;
for (;left_to_read > 0; ++file_iter, left_to_read -= read_bytes for (;left_to_read > 0; ++file_iter, left_to_read -= read_bytes
, buf_pos += read_bytes) , buf_pos += read_bytes)
@ -1047,7 +1088,6 @@ namespace libtorrent
if (file_iter->pad_file) if (file_iter->pad_file)
{ {
file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
int num_tmp_bufs = copy_bufs(current_buf, file_iter->size, tmp_bufs); int num_tmp_bufs = copy_bufs(current_buf, file_iter->size, tmp_bufs);
clear_bufs(tmp_bufs, num_tmp_bufs); clear_bufs(tmp_bufs, num_tmp_bufs);
advance_bufs(current_buf, file_iter->size); advance_bufs(current_buf, file_iter->size);
@ -1057,23 +1097,21 @@ namespace libtorrent
fs::path path = m_save_path / file_iter->path; fs::path path = m_save_path / file_iter->path;
error_code ec; error_code ec;
in = m_pool.open_file(this, path, file::read_only, ec); int mode = file::read_only;
if (io_thread().no_buffer()
&& ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0)
mode |= file::no_buffer;
in = m_pool.open_file(this, path, mode, ec);
if (!in || ec) if (!in || ec)
{ {
set_error(path, ec); set_error(path, ec);
return -1; return -1;
} }
size_type pos = in->seek(file_iter->file_base + file_offset, file::begin, ec);
if (pos != file_iter->file_base + file_offset || ec)
{
set_error(m_save_path / file_iter->path, ec);
return -1;
}
file_offset = 0;
file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
int num_tmp_bufs = copy_bufs(current_buf, read_bytes, tmp_bufs); int num_tmp_bufs = copy_bufs(current_buf, read_bytes, tmp_bufs);
int actual_read = int(in->readv(tmp_bufs, num_tmp_bufs, ec)); int actual_read = int(in->readv(file_iter->file_base
+ file_offset, tmp_bufs, num_tmp_bufs, ec));
file_offset = 0;
if (read_bytes != actual_read || ec) if (read_bytes != actual_read || ec)
{ {
@ -1094,6 +1132,8 @@ namespace libtorrent
, int offset , int offset
, int size) , int size)
{ {
// buffers must be page aligned
TORRENT_ASSERT((int(buf) % 4096) == 0);
file::iovec_t b = { (void*)buf, size }; file::iovec_t b = { (void*)buf, size };
return writev(&b, slot, offset, 1); return writev(&b, slot, offset, 1);
} }
@ -1104,12 +1144,14 @@ namespace libtorrent
, int offset , int offset
, int size) , int size)
{ {
// buffers must be page aligned
TORRENT_ASSERT((int(buf) % 4096) == 0);
file::iovec_t b = { (void*)buf, size }; file::iovec_t b = { (void*)buf, size };
return readv(&b, slot, offset, 1); return readv(&b, slot, offset, 1);
} }
int storage::writev( int storage::writev(
file::iovec_t* bufs file::iovec_t const* bufs
, int slot , int slot
, int offset , int offset
, int num_bufs) , int num_bufs)
@ -1162,7 +1204,9 @@ namespace libtorrent
int counter = 0; int counter = 0;
#endif #endif
file::iovec_t* current_buf = bufs; file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
file::iovec_t* current_buf = TORRENT_ALLOCA(file::iovec_t, num_bufs);
copy_bufs(bufs, size, current_buf);
int write_bytes; int write_bytes;
for (;left_to_write > 0; ++file_iter, left_to_write -= write_bytes for (;left_to_write > 0; ++file_iter, left_to_write -= write_bytes
, buf_pos += write_bytes) , buf_pos += write_bytes)
@ -1186,28 +1230,32 @@ namespace libtorrent
#endif #endif
if (file_iter->pad_file) if (file_iter->pad_file)
{
int actual_written = (std::min)(int(file_iter->size), left_to_write);
advance_bufs(current_buf, actual_written);
left_to_write -= actual_written;
continue; continue;
}
fs::path path = m_save_path / file_iter->path; fs::path path = m_save_path / file_iter->path;
error_code ec; error_code ec;
out = m_pool.open_file(this, path, file::read_write, ec); int mode = file::read_write;
if (io_thread().no_buffer()
&& ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0)
mode |= file::no_buffer;
out = m_pool.open_file(this, path, mode, ec);
if (!out || ec) if (!out || ec)
{ {
set_error(path, ec); set_error(path, ec);
return -1; return -1;
} }
size_type pos = out->seek(file_iter->file_base + file_offset, file::begin, ec);
if (pos != file_iter->file_base + file_offset || ec)
{
set_error(m_save_path / file_iter->path, ec);
return -1;
}
file_offset = 0;
file::iovec_t* tmp_bufs = TORRENT_ALLOCA(file::iovec_t, num_bufs);
int num_tmp_bufs = copy_bufs(current_buf, write_bytes, tmp_bufs); int num_tmp_bufs = copy_bufs(current_buf, write_bytes, tmp_bufs);
int actual_written = int(out->writev(tmp_bufs, num_tmp_bufs, ec)); int actual_written = int(out->writev(file_iter->file_base
+ file_offset, tmp_bufs, num_tmp_bufs, ec));
file_offset = 0;
if (write_bytes != actual_written || ec) if (write_bytes != actual_written || ec)
{ {
@ -1245,11 +1293,15 @@ namespace libtorrent
, m_state(state_none) , m_state(state_none)
, m_current_slot(0) , m_current_slot(0)
, m_out_of_place(false) , m_out_of_place(false)
, m_scratch_buffer(io, 0)
, m_scratch_buffer2(io, 0)
, m_scratch_piece(-1) , m_scratch_piece(-1)
, m_storage_constructor(sc) , m_storage_constructor(sc)
, m_piece_data(io, 0)
, m_io_thread(io) , m_io_thread(io)
, m_torrent(torrent) , m_torrent(torrent)
{ {
m_storage->m_io_thread = &m_io_thread;
} }
piece_manager::~piece_manager() piece_manager::~piece_manager()
@ -1586,7 +1638,7 @@ namespace libtorrent
} }
int piece_manager::identify_data( int piece_manager::identify_data(
const std::vector<char>& piece_data char const* piece_data
, int current_slot) , int current_slot)
{ {
// INVARIANT_CHECK; // INVARIANT_CHECK;
@ -1595,19 +1647,17 @@ namespace libtorrent
const int last_piece_size = static_cast<int>(m_files.piece_size( const int last_piece_size = static_cast<int>(m_files.piece_size(
m_files.num_pieces() - 1)); m_files.num_pieces() - 1));
TORRENT_ASSERT((int)piece_data.size() >= last_piece_size);
// calculate a small digest, with the same // calculate a small digest, with the same
// size as the last piece. And a large digest // size as the last piece. And a large digest
// which has the same size as a normal piece // which has the same size as a normal piece
hasher small_digest; hasher small_digest;
small_digest.update(&piece_data[0], last_piece_size); small_digest.update(piece_data, last_piece_size);
hasher large_digest(small_digest); hasher large_digest(small_digest);
TORRENT_ASSERT(piece_size - last_piece_size >= 0); TORRENT_ASSERT(piece_size - last_piece_size >= 0);
if (piece_size - last_piece_size > 0) if (piece_size - last_piece_size > 0)
{ {
large_digest.update( large_digest.update(
&piece_data[last_piece_size] piece_data + last_piece_size
, piece_size - last_piece_size); , piece_size - last_piece_size);
} }
sha1_hash large_hash = large_digest.final(); sha1_hash large_hash = large_digest.final();
@ -1785,8 +1835,8 @@ namespace libtorrent
return fatal_disk_error; return fatal_disk_error;
} }
m_state = state_finished; m_state = state_finished;
buffer().swap(m_scratch_buffer); m_scratch_buffer.reset();
buffer().swap(m_scratch_buffer2); m_scratch_buffer2.reset();
if (m_storage_mode != storage_mode_compact) if (m_storage_mode != storage_mode_compact)
{ {
// if no piece is out of place // if no piece is out of place
@ -2035,11 +2085,16 @@ namespace libtorrent
if (other_piece >= 0) if (other_piece >= 0)
{ {
if (m_scratch_buffer2.empty()) if (!m_scratch_buffer2)
m_scratch_buffer2.resize(m_files.piece_length()); {
int blocks_per_piece = (std::max)(m_files.piece_length()
/ m_io_thread.block_size(), 1);
m_scratch_buffer2.reset(m_io_thread.allocate_buffers(
blocks_per_piece), blocks_per_piece);
}
int piece_size = m_files.piece_size(other_piece); int piece_size = m_files.piece_size(other_piece);
if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size) if (m_storage->read(m_scratch_buffer2.get(), piece, 0, piece_size)
!= piece_size) != piece_size)
{ {
error = m_storage->error().message(); error = m_storage->error().message();
@ -2053,7 +2108,7 @@ namespace libtorrent
// the slot where this piece belongs is // the slot where this piece belongs is
// free. Just move the piece there. // free. Just move the piece there.
int piece_size = m_files.piece_size(piece); int piece_size = m_files.piece_size(piece);
if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size) if (m_storage->write(m_scratch_buffer.get(), piece, 0, piece_size) != piece_size)
{ {
error = m_storage->error().message(); error = m_storage->error().message();
TORRENT_ASSERT(!error.empty()); TORRENT_ASSERT(!error.empty());
@ -2091,11 +2146,15 @@ namespace libtorrent
// there is another piece in the slot // there is another piece in the slot
// where this one goes. Store it in the scratch // where this one goes. Store it in the scratch
// buffer until next iteration. // buffer until next iteration.
if (m_scratch_buffer.empty()) if (!m_scratch_buffer)
m_scratch_buffer.resize(m_files.piece_length()); {
int blocks_per_piece = (std::max)(m_files.piece_length() / m_io_thread.block_size(), 1);
m_scratch_buffer.reset(m_io_thread.allocate_buffers(
blocks_per_piece), blocks_per_piece);
}
int piece_size = m_files.piece_size(other_piece); int piece_size = m_files.piece_size(other_piece);
if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size) if (m_storage->read(m_scratch_buffer.get(), piece, 0, piece_size) != piece_size)
{ {
error = m_storage->error().message(); error = m_storage->error().message();
TORRENT_ASSERT(!error.empty()); TORRENT_ASSERT(!error.empty());
@ -2171,7 +2230,7 @@ namespace libtorrent
TORRENT_ASSERT(m_current_slot == m_files.num_pieces()); TORRENT_ASSERT(m_current_slot == m_files.num_pieces());
// clear the memory we've been using // clear the memory we've been using
std::vector<char>().swap(m_piece_data); m_piece_data.reset();
std::multimap<sha1_hash, int>().swap(m_hash_to_piece); std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
if (m_storage_mode != storage_mode_compact) if (m_storage_mode != storage_mode_compact)
@ -2226,16 +2285,22 @@ namespace libtorrent
m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i)); m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i));
} }
m_piece_data.resize(int(m_files.piece_length())); if (!m_piece_data)
{
int blocks_per_piece = (std::max)(m_files.piece_length() / m_io_thread.block_size(), 1);
m_piece_data.reset(m_io_thread.allocate_buffers(blocks_per_piece), blocks_per_piece);
}
int piece_size = m_files.piece_size(m_current_slot); int piece_size = m_files.piece_size(m_current_slot);
int num_read = m_storage->read(&m_piece_data[0] int num_read = m_storage->read(m_piece_data.get()
, m_current_slot, 0, piece_size); , m_current_slot, 0, piece_size);
if (num_read < 0) if (num_read < 0)
{ {
if (m_storage->error() if (m_storage->error()
#ifdef TORRENT_WINDOWS #ifdef TORRENT_WINDOWS
&& m_storage->error() != error_code(ERROR_FILE_NOT_FOUND, get_system_category())) && m_storage->error() != error_code(ERROR_FILE_NOT_FOUND, get_system_category())
&& m_storage->error() != error_code(ERROR_HANDLE_EOF, get_system_category()))
#else #else
&& m_storage->error() != error_code(ENOENT, get_posix_category())) && m_storage->error() != error_code(ENOENT, get_posix_category()))
#endif #endif
@ -2247,7 +2312,7 @@ namespace libtorrent
if (num_read != piece_size) if (num_read != piece_size)
return 1; return 1;
int piece_index = identify_data(m_piece_data, m_current_slot); int piece_index = identify_data(m_piece_data.get(), m_current_slot);
if (piece_index >= 0) have_piece = piece_index; if (piece_index >= 0) have_piece = piece_index;

View File

@ -252,16 +252,13 @@ namespace libtorrent
file f; file f;
error_code ec; error_code ec;
if (!f.open(filename, file::read_only, ec)) return -1; if (!f.open(filename, file::read_only, ec)) return -1;
f.seek(0, file::end, ec); size_type s = f.get_size(ec);
if (ec) return -1;
size_type s = f.tell(ec);
if (ec) return -1; if (ec) return -1;
if (s > 5000000) return -2; if (s > 5000000) return -2;
v.resize(s); v.resize(s);
if (s == 0) return 0; if (s == 0) return 0;
f.seek(0, file::begin, ec); file::iovec_t b = {&v[0], s};
if (ec) return -1; size_type read = f.readv(0, &b, 1, ec);
size_type read = f.read(&v[0], s, ec);
if (read != s) return -3; if (read != s) return -3;
if (ec) return -3; if (ec) return -3;
return 0; return 0;