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 <unistd.h>
#ifdef HAVE_MMAP
# include <sys/mman.h>
#endif // HAVE_MMAP
#include <cerrno>
#include <cstring>
@ -48,13 +51,17 @@
#include "fmt.h"
#include "DownloadFailureException.h"
#include "error_code.h"
#include "LogFactory.h"
namespace aria2 {
AbstractDiskWriter::AbstractDiskWriter(const std::string& filename)
: filename_(filename),
fd_(-1),
readOnly_(false)
readOnly_(false),
enableMmap_(false),
mapaddr_(0),
maplen_(0)
{}
AbstractDiskWriter::~AbstractDiskWriter()
@ -77,6 +84,20 @@ void AbstractDiskWriter::openFile(off_t totalLength)
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) {
close(fd_);
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;
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;
if(mapaddr_) {
memcpy(mapaddr_ + offset, data, len);
return len;
} else {
ssize_t writtenLength = 0;
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;
while((ret = read(fd_, data, len)) == -1 && errno == EINTR);
return ret;
if(mapaddr_) {
ssize_t readlen;
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)
@ -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)
{
seek(offset);
if(writeDataInternal(data, len) < 0) {
ensureMmapWrite(len, offset);
if(writeDataInternal(data, len, offset) < 0) {
int errNum = errno;
// If errno is ENOSPC(not enough space in device), throw
// 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 ret;
seek(offset);
if((ret = readDataInternal(data, len)) < 0) {
if((ret = readDataInternal(data, len, offset)) < 0) {
int errNum = errno;
throw DL_ABORT_EX3
(errNum,
@ -287,4 +361,9 @@ void AbstractDiskWriter::disableReadOnly()
readOnly_ = false;
}
void AbstractDiskWriter::enableMmap()
{
enableMmap_ = true;
}
} // namespace aria2

View File

@ -47,10 +47,17 @@ private:
bool readOnly_;
ssize_t writeDataInternal(const unsigned char* data, size_t len);
ssize_t readDataInternal(unsigned char* data, size_t len);
bool enableMmap_;
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 ensureMmapWrite(size_t len, off_t offset);
protected:
void createFile(int addFlags = 0);
public:
@ -77,6 +84,8 @@ public:
virtual void enableReadOnly();
virtual void disableReadOnly();
virtual void enableMmap();
};
} // namespace aria2

View File

@ -126,6 +126,11 @@ void AbstractSingleDiskAdaptor::disableReadOnly()
readOnly_ = false;
}
void AbstractSingleDiskAdaptor::enableMmap()
{
diskWriter_->enableMmap();
}
void AbstractSingleDiskAdaptor::cutTrailingGarbage()
{
if(File(getFilePath()).size() > totalLength_) {

View File

@ -80,6 +80,8 @@ public:
virtual void disableReadOnly();
virtual bool isReadOnlyEnabled() const { return readOnly_; }
virtual void enableMmap();
virtual void cutTrailingGarbage();

View File

@ -42,6 +42,10 @@
#include "DownloadEngine.h"
#include "DownloadContext.h"
#include "FileEntry.h"
#include "PieceStorage.h"
#include "DiskAdaptor.h"
#include "Option.h"
#include "prefs.h"
namespace aria2 {
@ -55,6 +59,9 @@ void BtFileAllocationEntry::prepareForNextAction
{
BtSetup().setup(commands, getRequestGroup(), e,
getRequestGroup()->getOption().get());
if(getRequestGroup()->getOption()->getAsBool(PREF_ENABLE_MMAP)) {
getRequestGroup()->getPieceStorage()->getDiskAdaptor()->enableMmap();
}
if(!getRequestGroup()->downloadFinished()) {
// For DownloadContext::resetDownloadStartTime(), see also
// RequestGroup::createInitialCommand()

View File

@ -87,6 +87,8 @@ public:
virtual bool isReadOnlyEnabled() const { return false; }
virtual void enableMmap() {}
// 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
// length.

View File

@ -79,6 +79,9 @@ public:
// opens file in read/write mode. This is an optional
// functionality. The default implementation is do noting.
virtual void disableReadOnly() {}
// Enables mmap.
virtual void enableMmap() {}
};
typedef SharedHandle<DiskWriter> DiskWriterHandle;

View File

@ -119,7 +119,8 @@ bool DiskWriterEntry::operator<(const DiskWriterEntry& entry) const
MultiDiskAdaptor::MultiDiskAdaptor()
: pieceLength_(0),
maxOpenFiles_(DEFAULT_MAX_OPEN_FILES),
readOnly_(false)
readOnly_(false),
enableMmap_(false)
{}
MultiDiskAdaptor::~MultiDiskAdaptor() {}
@ -230,6 +231,9 @@ void MultiDiskAdaptor::resetDiskWriterEntries()
if(readOnly_) {
(*i)->getDiskWriter()->enableReadOnly();
}
if(enableMmap_) {
(*i)->getDiskWriter()->enableMmap();
}
}
}
}
@ -451,6 +455,11 @@ void MultiDiskAdaptor::disableReadOnly()
readOnly_ = false;
}
void MultiDiskAdaptor::enableMmap()
{
enableMmap_ = true;
}
void MultiDiskAdaptor::cutTrailingGarbage()
{
for(std::vector<SharedHandle<DiskWriterEntry> >::const_iterator i =

View File

@ -112,6 +112,7 @@ private:
int maxOpenFiles_;
bool readOnly_;
bool enableMmap_;
void resetDiskWriterEntries();
@ -149,6 +150,8 @@ public:
virtual bool isReadOnlyEnabled() const { return readOnly_; }
virtual void enableMmap();
void setPieceLength(int32_t pieceLength)
{
pieceLength_ = pieceLength;

View File

@ -292,6 +292,21 @@ OptionHandlerFactory::createOptionHandlers()
op->addTag(TAG_FILE);
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
(PREF_ENABLE_RPC,

View File

@ -44,6 +44,8 @@
#include "Command.h"
#include "PeerStat.h"
#include "FileEntry.h"
#include "PieceStorage.h"
#include "DiskAdaptor.h"
namespace aria2 {
@ -61,6 +63,9 @@ void StreamFileAllocationEntry::prepareForNextAction
// For DownloadContext::resetDownloadStartTime(), see also
// RequestGroup::createInitialCommand()
getRequestGroup()->getDownloadContext()->resetDownloadStartTime();
if(getRequestGroup()->getOption()->getAsBool(PREF_ENABLE_MMAP)) {
getRequestGroup()->getPieceStorage()->getDiskAdaptor()->enableMmap();
}
if(getNextCommand()) {
// Reset download start time of PeerStat because it is started
// 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");
// value: pid
const Pref* PREF_STOP_WITH_PROCESS = makePref("stop-with-process");
// value: true | false
const Pref* PREF_ENABLE_MMAP = makePref("enable-mmap");
/**
* FTP related preferences

View File

@ -268,6 +268,8 @@ extern const Pref* PREF_HASH_CHECK_ONLY;
extern const Pref* PREF_CHECKSUM;
// value: pid
extern const Pref* PREF_STOP_WITH_PROCESS;
// value: true | false
extern const Pref* PREF_ENABLE_MMAP;
/**
* FTP related preferences

View File

@ -875,3 +875,5 @@
" selected. Please use this option with care\n" \
" because it will actually remove files from\n" \
" your disk.")
#define TEXT_ENABLE_MMAP \
_(" --enable-mmap[=true|false] Map files into memory.")