mirror of https://github.com/aria2/aria2
Added --enable-mmap option.
If this option is used, map files into memory using mmap(2). This option is experimental.pull/20/head
parent
38426d744b
commit
96720b297d
|
@ -35,6 +35,9 @@
|
||||||
#include "AbstractDiskWriter.h"
|
#include "AbstractDiskWriter.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
# include <sys/mman.h>
|
||||||
|
#endif // HAVE_MMAP
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -48,13 +51,17 @@
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
#include "DownloadFailureException.h"
|
#include "DownloadFailureException.h"
|
||||||
#include "error_code.h"
|
#include "error_code.h"
|
||||||
|
#include "LogFactory.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
AbstractDiskWriter::AbstractDiskWriter(const std::string& filename)
|
AbstractDiskWriter::AbstractDiskWriter(const std::string& filename)
|
||||||
: filename_(filename),
|
: filename_(filename),
|
||||||
fd_(-1),
|
fd_(-1),
|
||||||
readOnly_(false)
|
readOnly_(false),
|
||||||
|
enableMmap_(false),
|
||||||
|
mapaddr_(0),
|
||||||
|
maplen_(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
AbstractDiskWriter::~AbstractDiskWriter()
|
AbstractDiskWriter::~AbstractDiskWriter()
|
||||||
|
@ -77,6 +84,20 @@ void AbstractDiskWriter::openFile(off_t totalLength)
|
||||||
|
|
||||||
void AbstractDiskWriter::closeFile()
|
void AbstractDiskWriter::closeFile()
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
if(mapaddr_) {
|
||||||
|
int rv = munmap(mapaddr_, maplen_);
|
||||||
|
if(rv == -1) {
|
||||||
|
int errNum = errno;
|
||||||
|
A2_LOG_ERROR(fmt("munmap for file %s failed: %s",
|
||||||
|
filename_.c_str(), strerror(errNum)));
|
||||||
|
} else {
|
||||||
|
A2_LOG_INFO(fmt("munmap for file %s succeeded", filename_.c_str()));
|
||||||
|
}
|
||||||
|
mapaddr_ = 0;
|
||||||
|
maplen_ = 0;
|
||||||
|
}
|
||||||
|
#endif // HAVE_MMAP
|
||||||
if(fd_ != -1) {
|
if(fd_ != -1) {
|
||||||
close(fd_);
|
close(fd_);
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
|
@ -124,25 +145,46 @@ void AbstractDiskWriter::createFile(int addFlags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t AbstractDiskWriter::writeDataInternal(const unsigned char* data, size_t len)
|
ssize_t AbstractDiskWriter::writeDataInternal(const unsigned char* data,
|
||||||
|
size_t len, off_t offset)
|
||||||
{
|
{
|
||||||
ssize_t writtenLength = 0;
|
if(mapaddr_) {
|
||||||
while((size_t)writtenLength < len) {
|
memcpy(mapaddr_ + offset, data, len);
|
||||||
ssize_t ret = 0;
|
return len;
|
||||||
while((ret = write(fd_, data+writtenLength, len-writtenLength)) == -1 && errno == EINTR);
|
} else {
|
||||||
if(ret == -1) {
|
ssize_t writtenLength = 0;
|
||||||
return -1;
|
seek(offset);
|
||||||
|
while((size_t)writtenLength < len) {
|
||||||
|
ssize_t ret = 0;
|
||||||
|
while((ret = write(fd_, data+writtenLength, len-writtenLength)) == -1 &&
|
||||||
|
errno == EINTR);
|
||||||
|
if(ret == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
writtenLength += ret;
|
||||||
}
|
}
|
||||||
writtenLength += ret;
|
return writtenLength;
|
||||||
}
|
}
|
||||||
return writtenLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t AbstractDiskWriter::readDataInternal(unsigned char* data, size_t len)
|
ssize_t AbstractDiskWriter::readDataInternal(unsigned char* data, size_t len,
|
||||||
|
off_t offset)
|
||||||
{
|
{
|
||||||
ssize_t ret = 0;
|
if(mapaddr_) {
|
||||||
while((ret = read(fd_, data, len)) == -1 && errno == EINTR);
|
ssize_t readlen;
|
||||||
return ret;
|
if(offset > maplen_) {
|
||||||
|
readlen = 0;
|
||||||
|
} else {
|
||||||
|
readlen = std::min(static_cast<size_t>(maplen_ - offset), len);
|
||||||
|
}
|
||||||
|
memcpy(data, mapaddr_ + offset, readlen);
|
||||||
|
return readlen;
|
||||||
|
} else {
|
||||||
|
ssize_t ret = 0;
|
||||||
|
seek(offset);
|
||||||
|
while((ret = read(fd_, data, len)) == -1 && errno == EINTR);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractDiskWriter::seek(off_t offset)
|
void AbstractDiskWriter::seek(off_t offset)
|
||||||
|
@ -156,10 +198,43 @@ void AbstractDiskWriter::seek(off_t offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AbstractDiskWriter::ensureMmapWrite(size_t len, off_t offset)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
if(enableMmap_) {
|
||||||
|
if(mapaddr_) {
|
||||||
|
if(static_cast<off_t>(len + offset) > maplen_) {
|
||||||
|
munmap(mapaddr_, maplen_);
|
||||||
|
mapaddr_ = 0;
|
||||||
|
maplen_ = 0;
|
||||||
|
enableMmap_ = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
off_t filesize = size();
|
||||||
|
if(static_cast<off_t>(len + offset) <= filesize) {
|
||||||
|
mapaddr_ = reinterpret_cast<unsigned char*>
|
||||||
|
(mmap(0, size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0));
|
||||||
|
if(mapaddr_) {
|
||||||
|
A2_LOG_DEBUG(fmt("mmap for file %s succeeded, length=%lld",
|
||||||
|
filename_.c_str(),
|
||||||
|
static_cast<long long int>(filesize)));
|
||||||
|
maplen_ = filesize;
|
||||||
|
} else {
|
||||||
|
int errNum = errno;
|
||||||
|
A2_LOG_INFO(fmt("mmap for file %s failed: %s",
|
||||||
|
filename_.c_str(), strerror(errNum)));
|
||||||
|
enableMmap_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // HAVE_MMAP
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractDiskWriter::writeData(const unsigned char* data, size_t len, off_t offset)
|
void AbstractDiskWriter::writeData(const unsigned char* data, size_t len, off_t offset)
|
||||||
{
|
{
|
||||||
seek(offset);
|
ensureMmapWrite(len, offset);
|
||||||
if(writeDataInternal(data, len) < 0) {
|
if(writeDataInternal(data, len, offset) < 0) {
|
||||||
int errNum = errno;
|
int errNum = errno;
|
||||||
// If errno is ENOSPC(not enough space in device), throw
|
// If errno is ENOSPC(not enough space in device), throw
|
||||||
// DownloadFailureException and abort download instantly.
|
// DownloadFailureException and abort download instantly.
|
||||||
|
@ -184,8 +259,7 @@ void AbstractDiskWriter::writeData(const unsigned char* data, size_t len, off_t
|
||||||
ssize_t AbstractDiskWriter::readData(unsigned char* data, size_t len, off_t offset)
|
ssize_t AbstractDiskWriter::readData(unsigned char* data, size_t len, off_t offset)
|
||||||
{
|
{
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
seek(offset);
|
if((ret = readDataInternal(data, len, offset)) < 0) {
|
||||||
if((ret = readDataInternal(data, len)) < 0) {
|
|
||||||
int errNum = errno;
|
int errNum = errno;
|
||||||
throw DL_ABORT_EX3
|
throw DL_ABORT_EX3
|
||||||
(errNum,
|
(errNum,
|
||||||
|
@ -287,4 +361,9 @@ void AbstractDiskWriter::disableReadOnly()
|
||||||
readOnly_ = false;
|
readOnly_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AbstractDiskWriter::enableMmap()
|
||||||
|
{
|
||||||
|
enableMmap_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -47,10 +47,17 @@ private:
|
||||||
|
|
||||||
bool readOnly_;
|
bool readOnly_;
|
||||||
|
|
||||||
ssize_t writeDataInternal(const unsigned char* data, size_t len);
|
bool enableMmap_;
|
||||||
ssize_t readDataInternal(unsigned char* data, size_t len);
|
unsigned char* mapaddr_;
|
||||||
|
off_t maplen_;
|
||||||
|
|
||||||
|
ssize_t writeDataInternal(const unsigned char* data, size_t len,
|
||||||
|
off_t offset);
|
||||||
|
ssize_t readDataInternal(unsigned char* data, size_t len, off_t offset);
|
||||||
|
|
||||||
void seek(off_t offset);
|
void seek(off_t offset);
|
||||||
|
|
||||||
|
void ensureMmapWrite(size_t len, off_t offset);
|
||||||
protected:
|
protected:
|
||||||
void createFile(int addFlags = 0);
|
void createFile(int addFlags = 0);
|
||||||
public:
|
public:
|
||||||
|
@ -77,6 +84,8 @@ public:
|
||||||
virtual void enableReadOnly();
|
virtual void enableReadOnly();
|
||||||
|
|
||||||
virtual void disableReadOnly();
|
virtual void disableReadOnly();
|
||||||
|
|
||||||
|
virtual void enableMmap();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -126,6 +126,11 @@ void AbstractSingleDiskAdaptor::disableReadOnly()
|
||||||
readOnly_ = false;
|
readOnly_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AbstractSingleDiskAdaptor::enableMmap()
|
||||||
|
{
|
||||||
|
diskWriter_->enableMmap();
|
||||||
|
}
|
||||||
|
|
||||||
void AbstractSingleDiskAdaptor::cutTrailingGarbage()
|
void AbstractSingleDiskAdaptor::cutTrailingGarbage()
|
||||||
{
|
{
|
||||||
if(File(getFilePath()).size() > totalLength_) {
|
if(File(getFilePath()).size() > totalLength_) {
|
||||||
|
|
|
@ -80,6 +80,8 @@ public:
|
||||||
virtual void disableReadOnly();
|
virtual void disableReadOnly();
|
||||||
|
|
||||||
virtual bool isReadOnlyEnabled() const { return readOnly_; }
|
virtual bool isReadOnlyEnabled() const { return readOnly_; }
|
||||||
|
|
||||||
|
virtual void enableMmap();
|
||||||
|
|
||||||
virtual void cutTrailingGarbage();
|
virtual void cutTrailingGarbage();
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@
|
||||||
#include "DownloadEngine.h"
|
#include "DownloadEngine.h"
|
||||||
#include "DownloadContext.h"
|
#include "DownloadContext.h"
|
||||||
#include "FileEntry.h"
|
#include "FileEntry.h"
|
||||||
|
#include "PieceStorage.h"
|
||||||
|
#include "DiskAdaptor.h"
|
||||||
|
#include "Option.h"
|
||||||
|
#include "prefs.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
@ -55,6 +59,9 @@ void BtFileAllocationEntry::prepareForNextAction
|
||||||
{
|
{
|
||||||
BtSetup().setup(commands, getRequestGroup(), e,
|
BtSetup().setup(commands, getRequestGroup(), e,
|
||||||
getRequestGroup()->getOption().get());
|
getRequestGroup()->getOption().get());
|
||||||
|
if(getRequestGroup()->getOption()->getAsBool(PREF_ENABLE_MMAP)) {
|
||||||
|
getRequestGroup()->getPieceStorage()->getDiskAdaptor()->enableMmap();
|
||||||
|
}
|
||||||
if(!getRequestGroup()->downloadFinished()) {
|
if(!getRequestGroup()->downloadFinished()) {
|
||||||
// For DownloadContext::resetDownloadStartTime(), see also
|
// For DownloadContext::resetDownloadStartTime(), see also
|
||||||
// RequestGroup::createInitialCommand()
|
// RequestGroup::createInitialCommand()
|
||||||
|
|
|
@ -87,6 +87,8 @@ public:
|
||||||
|
|
||||||
virtual bool isReadOnlyEnabled() const { return false; }
|
virtual bool isReadOnlyEnabled() const { return false; }
|
||||||
|
|
||||||
|
virtual void enableMmap() {}
|
||||||
|
|
||||||
// Assumed each file length is stored in fileEntries or DiskAdaptor knows it.
|
// Assumed each file length is stored in fileEntries or DiskAdaptor knows it.
|
||||||
// If each actual file's length is larger than that, truncate file to that
|
// If each actual file's length is larger than that, truncate file to that
|
||||||
// length.
|
// length.
|
||||||
|
|
|
@ -79,6 +79,9 @@ public:
|
||||||
// opens file in read/write mode. This is an optional
|
// opens file in read/write mode. This is an optional
|
||||||
// functionality. The default implementation is do noting.
|
// functionality. The default implementation is do noting.
|
||||||
virtual void disableReadOnly() {}
|
virtual void disableReadOnly() {}
|
||||||
|
|
||||||
|
// Enables mmap.
|
||||||
|
virtual void enableMmap() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef SharedHandle<DiskWriter> DiskWriterHandle;
|
typedef SharedHandle<DiskWriter> DiskWriterHandle;
|
||||||
|
|
|
@ -119,7 +119,8 @@ bool DiskWriterEntry::operator<(const DiskWriterEntry& entry) const
|
||||||
MultiDiskAdaptor::MultiDiskAdaptor()
|
MultiDiskAdaptor::MultiDiskAdaptor()
|
||||||
: pieceLength_(0),
|
: pieceLength_(0),
|
||||||
maxOpenFiles_(DEFAULT_MAX_OPEN_FILES),
|
maxOpenFiles_(DEFAULT_MAX_OPEN_FILES),
|
||||||
readOnly_(false)
|
readOnly_(false),
|
||||||
|
enableMmap_(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
MultiDiskAdaptor::~MultiDiskAdaptor() {}
|
MultiDiskAdaptor::~MultiDiskAdaptor() {}
|
||||||
|
@ -230,6 +231,9 @@ void MultiDiskAdaptor::resetDiskWriterEntries()
|
||||||
if(readOnly_) {
|
if(readOnly_) {
|
||||||
(*i)->getDiskWriter()->enableReadOnly();
|
(*i)->getDiskWriter()->enableReadOnly();
|
||||||
}
|
}
|
||||||
|
if(enableMmap_) {
|
||||||
|
(*i)->getDiskWriter()->enableMmap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,6 +455,11 @@ void MultiDiskAdaptor::disableReadOnly()
|
||||||
readOnly_ = false;
|
readOnly_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiDiskAdaptor::enableMmap()
|
||||||
|
{
|
||||||
|
enableMmap_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
void MultiDiskAdaptor::cutTrailingGarbage()
|
void MultiDiskAdaptor::cutTrailingGarbage()
|
||||||
{
|
{
|
||||||
for(std::vector<SharedHandle<DiskWriterEntry> >::const_iterator i =
|
for(std::vector<SharedHandle<DiskWriterEntry> >::const_iterator i =
|
||||||
|
|
|
@ -112,6 +112,7 @@ private:
|
||||||
int maxOpenFiles_;
|
int maxOpenFiles_;
|
||||||
|
|
||||||
bool readOnly_;
|
bool readOnly_;
|
||||||
|
bool enableMmap_;
|
||||||
|
|
||||||
void resetDiskWriterEntries();
|
void resetDiskWriterEntries();
|
||||||
|
|
||||||
|
@ -149,6 +150,8 @@ public:
|
||||||
|
|
||||||
virtual bool isReadOnlyEnabled() const { return readOnly_; }
|
virtual bool isReadOnlyEnabled() const { return readOnly_; }
|
||||||
|
|
||||||
|
virtual void enableMmap();
|
||||||
|
|
||||||
void setPieceLength(int32_t pieceLength)
|
void setPieceLength(int32_t pieceLength)
|
||||||
{
|
{
|
||||||
pieceLength_ = pieceLength;
|
pieceLength_ = pieceLength;
|
||||||
|
|
|
@ -292,6 +292,21 @@ OptionHandlerFactory::createOptionHandlers()
|
||||||
op->addTag(TAG_FILE);
|
op->addTag(TAG_FILE);
|
||||||
handlers.push_back(op);
|
handlers.push_back(op);
|
||||||
}
|
}
|
||||||
|
#ifdef HAVE_MMAP
|
||||||
|
{
|
||||||
|
SharedHandle<OptionHandler> op(new BooleanOptionHandler
|
||||||
|
(PREF_ENABLE_MMAP,
|
||||||
|
TEXT_ENABLE_MMAP,
|
||||||
|
A2_V_FALSE,
|
||||||
|
OptionHandler::OPT_ARG));
|
||||||
|
op->addTag(TAG_ADVANCED);
|
||||||
|
op->addTag(TAG_EXPERIMENTAL);
|
||||||
|
op->setInitialOption(true);
|
||||||
|
op->setChangeGlobalOption(true);
|
||||||
|
op->setChangeOptionForReserved(true);
|
||||||
|
handlers.push_back(op);
|
||||||
|
}
|
||||||
|
#endif // HAVE_MMAP
|
||||||
{
|
{
|
||||||
SharedHandle<OptionHandler> op(new BooleanOptionHandler
|
SharedHandle<OptionHandler> op(new BooleanOptionHandler
|
||||||
(PREF_ENABLE_RPC,
|
(PREF_ENABLE_RPC,
|
||||||
|
|
|
@ -44,6 +44,8 @@
|
||||||
#include "Command.h"
|
#include "Command.h"
|
||||||
#include "PeerStat.h"
|
#include "PeerStat.h"
|
||||||
#include "FileEntry.h"
|
#include "FileEntry.h"
|
||||||
|
#include "PieceStorage.h"
|
||||||
|
#include "DiskAdaptor.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
@ -61,6 +63,9 @@ void StreamFileAllocationEntry::prepareForNextAction
|
||||||
// For DownloadContext::resetDownloadStartTime(), see also
|
// For DownloadContext::resetDownloadStartTime(), see also
|
||||||
// RequestGroup::createInitialCommand()
|
// RequestGroup::createInitialCommand()
|
||||||
getRequestGroup()->getDownloadContext()->resetDownloadStartTime();
|
getRequestGroup()->getDownloadContext()->resetDownloadStartTime();
|
||||||
|
if(getRequestGroup()->getOption()->getAsBool(PREF_ENABLE_MMAP)) {
|
||||||
|
getRequestGroup()->getPieceStorage()->getDiskAdaptor()->enableMmap();
|
||||||
|
}
|
||||||
if(getNextCommand()) {
|
if(getNextCommand()) {
|
||||||
// Reset download start time of PeerStat because it is started
|
// Reset download start time of PeerStat because it is started
|
||||||
// before file allocation begins.
|
// before file allocation begins.
|
||||||
|
|
|
@ -325,6 +325,8 @@ const Pref* PREF_HASH_CHECK_ONLY = makePref("hash-check-only");
|
||||||
const Pref* PREF_CHECKSUM = makePref("checksum");
|
const Pref* PREF_CHECKSUM = makePref("checksum");
|
||||||
// value: pid
|
// value: pid
|
||||||
const Pref* PREF_STOP_WITH_PROCESS = makePref("stop-with-process");
|
const Pref* PREF_STOP_WITH_PROCESS = makePref("stop-with-process");
|
||||||
|
// value: true | false
|
||||||
|
const Pref* PREF_ENABLE_MMAP = makePref("enable-mmap");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FTP related preferences
|
* FTP related preferences
|
||||||
|
|
|
@ -268,6 +268,8 @@ extern const Pref* PREF_HASH_CHECK_ONLY;
|
||||||
extern const Pref* PREF_CHECKSUM;
|
extern const Pref* PREF_CHECKSUM;
|
||||||
// value: pid
|
// value: pid
|
||||||
extern const Pref* PREF_STOP_WITH_PROCESS;
|
extern const Pref* PREF_STOP_WITH_PROCESS;
|
||||||
|
// value: true | false
|
||||||
|
extern const Pref* PREF_ENABLE_MMAP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FTP related preferences
|
* FTP related preferences
|
||||||
|
|
|
@ -875,3 +875,5 @@
|
||||||
" selected. Please use this option with care\n" \
|
" selected. Please use this option with care\n" \
|
||||||
" because it will actually remove files from\n" \
|
" because it will actually remove files from\n" \
|
||||||
" your disk.")
|
" your disk.")
|
||||||
|
#define TEXT_ENABLE_MMAP \
|
||||||
|
_(" --enable-mmap[=true|false] Map files into memory.")
|
||||||
|
|
Loading…
Reference in New Issue