From 631f37433a8fda03eb057e9f01e2811111c60d5e Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 31 May 2014 16:14:33 +0900 Subject: [PATCH] Save session only when there is change since the last serialization This is a slight optimization not to cause useless disk access. This only applies to saving session automatically (see --save-session-interval). aria2.saveSession and serialization at the end of the session are always performed as before. When serialization, we first check that whether there is any change since the last serialization. To do this, we first calculate hash value of serialized content without writing into file. Then compare this value to the value of last serialization. If they do not match, perform serialization. --- src/RequestGroupMan.cc | 1 + src/RequestGroupMan.h | 13 ++++++ src/SaveSessionCommand.cc | 15 ++++++- src/SessionSerializer.cc | 86 +++++++++++++++++++++++++++++++++++++++ src/SessionSerializer.h | 4 ++ 5 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src/RequestGroupMan.cc b/src/RequestGroupMan.cc index ac2eba8f..92c3b8c6 100644 --- a/src/RequestGroupMan.cc +++ b/src/RequestGroupMan.cc @@ -81,6 +81,7 @@ #include "PieceStorage.h" #include "DiskAdaptor.h" #include "SimpleRandomizer.h" +#include "array_fun.h" #ifdef ENABLE_BITTORRENT # include "bittorrent_helper.h" #endif // ENABLE_BITTORRENT diff --git a/src/RequestGroupMan.h b/src/RequestGroupMan.h index bbee4529..773184e8 100644 --- a/src/RequestGroupMan.h +++ b/src/RequestGroupMan.h @@ -110,6 +110,9 @@ private: // evicted DownloadResults. size_t numStoppedTotal_; + // SHA1 hash value of the content of last session serialization. + std::string lastSessionHash_; + void formatDownloadResultFull (OutputFile& out, const char* status, @@ -368,6 +371,16 @@ public: { return numStoppedTotal_; } + + void setLastSessionHash(std::string lastSessionHash) + { + lastSessionHash_ = std::move(lastSessionHash); + } + + const std::string& getLastSessionHash() const + { + return lastSessionHash_; + } }; } // namespace aria2 diff --git a/src/SaveSessionCommand.cc b/src/SaveSessionCommand.cc index d94815ad..a3b12e54 100644 --- a/src/SaveSessionCommand.cc +++ b/src/SaveSessionCommand.cc @@ -63,8 +63,19 @@ void SaveSessionCommand::process() const std::string& filename = getDownloadEngine()->getOption() ->get(PREF_SAVE_SESSION); if(!filename.empty()) { - SessionSerializer sessionSerializer(getDownloadEngine()-> - getRequestGroupMan().get()); + auto& rgman = getDownloadEngine()->getRequestGroupMan(); + + SessionSerializer sessionSerializer(rgman.get()); + + auto sessionHash = sessionSerializer.calculateHash(); + if(rgman->getLastSessionHash() == sessionHash) { + A2_LOG_INFO("No change since last serialization or startup. " + "No serialization is necessary this time."); + return; + } + + rgman->setLastSessionHash(std::move(sessionHash)); + if(sessionSerializer.save(filename)) { A2_LOG_NOTICE(fmt(_("Serialized session to '%s' successfully."), filename.c_str())); diff --git a/src/SessionSerializer.cc b/src/SessionSerializer.cc index cf02548b..9e149f0a 100644 --- a/src/SessionSerializer.cc +++ b/src/SessionSerializer.cc @@ -35,6 +35,7 @@ #include "SessionSerializer.h" #include +#include #include #include @@ -52,6 +53,7 @@ #include "BufferedFile.h" #include "OptionParser.h" #include "OptionHandler.h" +#include "MessageDigest.h" #if HAVE_ZLIB #include "GZipFile.h" @@ -325,4 +327,88 @@ bool SessionSerializer::save(IOFile& fp) const return true; } +namespace { +// Class to calculate SHA1 hash value for data written into this +// object. No file I/O is done in this class. +class SHA1IOFile : public IOFile { +public: + SHA1IOFile() + : sha1_(MessageDigest::sha1()) + {} + + std::string digest() + { + return sha1_->digest(); + } +protected: + virtual size_t onRead(void* ptr, size_t count) CXX11_OVERRIDE + { + assert(0); + } + + virtual size_t onWrite(const void* ptr, size_t count) CXX11_OVERRIDE + { + sha1_->update(ptr, count); + + return count; + } + + virtual char* onGets(char* s, int size) CXX11_OVERRIDE + { + assert(0); + } + + virtual int onVprintf(const char* format, va_list va) CXX11_OVERRIDE + { + assert(0); + } + + virtual int onFlush() CXX11_OVERRIDE + { + return 0; + } + + virtual int onClose() CXX11_OVERRIDE + { + return 0; + } + + virtual bool onSupportsColor() CXX11_OVERRIDE + { + return false; + } + + virtual bool isError() const CXX11_OVERRIDE + { + return false; + } + + virtual bool isEOF() const CXX11_OVERRIDE + { + return false; + } + + virtual bool isOpen() const CXX11_OVERRIDE + { + return true; + } + +private: + std::unique_ptr sha1_; +}; +} // namespace + +std::string SessionSerializer::calculateHash() const +{ + SHA1IOFile sha1io; + + auto rv = save(sha1io); + + if(!rv) { + return ""; + } + + return sha1io.digest(); +} + } // namespace aria2 diff --git a/src/SessionSerializer.h b/src/SessionSerializer.h index c3493edc..c9dbe29e 100644 --- a/src/SessionSerializer.h +++ b/src/SessionSerializer.h @@ -57,6 +57,10 @@ public: SessionSerializer(RequestGroupMan* requestGroupMan); bool save(const std::string& filename) const; + + // Calculates and returns SHA1 hash of the contents being + // serialized. + std::string calculateHash() const; }; } // namespace aria2