Added --enable-mmap option.

If this option is used, map files into memory using mmap(2).  This
option is experimental.
pull/20/head
Tatsuhiro Tsujikawa 2012-06-23 21:28:41 +09:00
parent 38426d744b
commit 96720b297d
14 changed files with 166 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.")