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 <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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -126,6 +126,11 @@ void AbstractSingleDiskAdaptor::disableReadOnly()
|
|||
readOnly_ = false;
|
||||
}
|
||||
|
||||
void AbstractSingleDiskAdaptor::enableMmap()
|
||||
{
|
||||
diskWriter_->enableMmap();
|
||||
}
|
||||
|
||||
void AbstractSingleDiskAdaptor::cutTrailingGarbage()
|
||||
{
|
||||
if(File(getFilePath()).size() > totalLength_) {
|
||||
|
|
|
@ -80,6 +80,8 @@ public:
|
|||
virtual void disableReadOnly();
|
||||
|
||||
virtual bool isReadOnlyEnabled() const { return readOnly_; }
|
||||
|
||||
virtual void enableMmap();
|
||||
|
||||
virtual void cutTrailingGarbage();
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.")
|
||||
|
|
Loading…
Reference in New Issue