updated disk IO to support unbuffered files
This commit is contained in:
parent
20a0593fa3
commit
7592ad4aee
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
--------------
|
--------------
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
{
|
{
|
||||||
|
|
526
src/file.cpp
526
src/file.cpp
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
287
src/storage.cpp
287
src/storage.cpp
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue