Ver Fonte

Added --enable-mmap option.

If this option is used, map files into memory using mmap(2).  This
option is experimental.
Tatsuhiro Tsujikawa há 13 anos atrás
pai
commit
96720b297d

+ 97 - 18
src/AbstractDiskWriter.cc

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

+ 11 - 2
src/AbstractDiskWriter.h

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

+ 5 - 0
src/AbstractSingleDiskAdaptor.cc

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

+ 2 - 0
src/AbstractSingleDiskAdaptor.h

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

+ 7 - 0
src/BtFileAllocationEntry.cc

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

+ 2 - 0
src/DiskAdaptor.h

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

+ 3 - 0
src/DiskWriter.h

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

+ 10 - 1
src/MultiDiskAdaptor.cc

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

+ 3 - 0
src/MultiDiskAdaptor.h

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

+ 15 - 0
src/OptionHandlerFactory.cc

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

+ 5 - 0
src/StreamFileAllocationEntry.cc

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

+ 2 - 0
src/prefs.cc

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

+ 2 - 0
src/prefs.h

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

+ 2 - 0
src/usage_text.h

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