diff --git a/doc/manual-src/en/aria2c.rst b/doc/manual-src/en/aria2c.rst index 2a9f3ccf48..e065695e33 100644 --- a/doc/manual-src/en/aria2c.rst +++ b/doc/manual-src/en/aria2c.rst @@ -595,6 +595,16 @@ BitTorrent/Metalink Options BitTorrent Specific Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. option:: --bt-detach-seed-only[=true|false] + + Exclude seed only downloads when counting concurrent active + downloads (See :option:`-j` option). This means that if ``-j3`` is + given and this option is turned on and 3 downloads are active and + one of those enters seed mode, then it is excluded from active + download count (thus it becomes 2), and the next download waiting in + queue gets started. But be aware that seeding item is still + recognized as active download in RPC method. Default: ``false`` + .. option:: --bt-enable-lpd[=true|false] Enable Local Peer Discovery. If a private flag is set in a torrent, diff --git a/src/BtFileAllocationEntry.cc b/src/BtFileAllocationEntry.cc index 4b1f41477e..5063f989fe 100644 --- a/src/BtFileAllocationEntry.cc +++ b/src/BtFileAllocationEntry.cc @@ -89,6 +89,7 @@ void BtFileAllocationEntry::prepareForNextAction diskAdaptor->openFile(); } #endif // __MINGW32__ + getRequestGroup()->enableSeedOnly(); } } diff --git a/src/DefaultPieceStorage.cc b/src/DefaultPieceStorage.cc index 1191a5a0b6..713e8365e9 100644 --- a/src/DefaultPieceStorage.cc +++ b/src/DefaultPieceStorage.cc @@ -490,11 +490,14 @@ void DefaultPieceStorage::completePiece(const std::shared_ptr& piece) diskAdaptor_->enableReadOnly(); diskAdaptor_->openFile(); #endif // __MINGW32__ - util::executeHookByOptName(downloadContext_->getOwnerRequestGroup(), - option_, PREF_ON_BT_DOWNLOAD_COMPLETE); + auto group = downloadContext_->getOwnerRequestGroup(); + + util::executeHookByOptName(group, option_, + PREF_ON_BT_DOWNLOAD_COMPLETE); SingletonHolder::instance()-> - notifyDownloadEvent(EVENT_ON_BT_DOWNLOAD_COMPLETE, - downloadContext_->getOwnerRequestGroup()); + notifyDownloadEvent(EVENT_ON_BT_DOWNLOAD_COMPLETE, group); + + group->enableSeedOnly(); } } #endif // ENABLE_BITTORRENT diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index f89a1c440d..dec0624ac3 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -1700,6 +1700,15 @@ std::vector OptionHandlerFactory::createOptionHandlers() #endif // ENABLE_BITTORRENT || ENABLE_METALINK // BitTorrent Specific Options #ifdef ENABLE_BITTORRENT + { + OptionHandler* op(new BooleanOptionHandler + (PREF_BT_DETACH_SEED_ONLY, + TEXT_BT_DETACH_SEED_ONLY, + A2_V_FALSE, + OptionHandler::OPT_ARG)); + op->addTag(TAG_BITTORRENT); + handlers.push_back(op); + } { OptionHandler* op(new BooleanOptionHandler (PREF_BT_ENABLE_LPD, diff --git a/src/RequestGroup.cc b/src/RequestGroup.cc index e06decdcba..d839286e30 100644 --- a/src/RequestGroup.cc +++ b/src/RequestGroup.cc @@ -149,7 +149,8 @@ RequestGroup::RequestGroup(const std::shared_ptr& gid, haltRequested_(false), forceHaltRequested_(false), pauseRequested_(false), - inMemoryDownload_(false) + inMemoryDownload_(false), + seedOnly_(false) { fileAllocationEnabled_ = option_->get(PREF_FILE_ALLOCATION) != V_NONE; if(!option_->getAsBool(PREF_DRY_RUN)) { @@ -1265,4 +1266,18 @@ bool RequestGroup::p2pInvolved() const #endif // !ENABLE_BITTORRENT } +void RequestGroup::enableSeedOnly() +{ + if(seedOnly_ || !option_->getAsBool(PREF_BT_DETACH_SEED_ONLY)) { + return; + } + + if(requestGroupMan_) { + seedOnly_ = true; + + requestGroupMan_->decreaseNumActive(); + requestGroupMan_->requestQueueCheck(); + } +} + } // namespace aria2 diff --git a/src/RequestGroup.h b/src/RequestGroup.h index b04cfafc19..0096674807 100644 --- a/src/RequestGroup.h +++ b/src/RequestGroup.h @@ -179,6 +179,7 @@ class RequestGroup { // just sits in memory. bool inMemoryDownload_; + bool seedOnly_; void validateFilename(const std::string& expectedFilename, const std::string& actualFilename) const; @@ -563,6 +564,13 @@ class RequestGroup { { state_ = state; } + + bool isSeedOnlyEnabled() + { + return seedOnly_; + } + + void enableSeedOnly(); }; } // namespace aria2 diff --git a/src/RequestGroupMan.cc b/src/RequestGroupMan.cc index 8921bfe1cf..b33e79e011 100644 --- a/src/RequestGroupMan.cc +++ b/src/RequestGroupMan.cc @@ -105,6 +105,7 @@ RequestGroupMan::RequestGroupMan int maxSimultaneousDownloads, const Option* option) : maxSimultaneousDownloads_(maxSimultaneousDownloads), + numActive_(0), option_(option), serverStatMan_(std::make_shared()), maxOverallDownloadSpeedLimit_ @@ -140,6 +141,7 @@ bool RequestGroupMan::downloadFinished() void RequestGroupMan::addRequestGroup (const std::shared_ptr& group) { + ++numActive_; requestGroups_.push_back(group->getGID(), group); } @@ -320,6 +322,11 @@ class ProcessStoppedRequestGroup { if(group->getNumCommand() == 0) { collectStat(group); const std::shared_ptr& dctx = group->getDownloadContext(); + + if(!group->isSeedOnlyEnabled()) { + e_->getRequestGroupMan()->decreaseNumActive(); + } + // DownloadContext::resetDownloadStopTime() is only called when // download completed. If // DownloadContext::getDownloadStopTime().isZero() is true, then @@ -453,11 +460,11 @@ std::vector> createInitialCommand void RequestGroupMan::fillRequestGroupFromReserver(DownloadEngine* e) { removeStoppedGroup(e); - if(static_cast(maxSimultaneousDownloads_) <= requestGroups_.size()) { + if(static_cast(maxSimultaneousDownloads_) <= numActive_) { return; } int count = 0; - int num = maxSimultaneousDownloads_-requestGroups_.size(); + int num = maxSimultaneousDownloads_ - numActive_; std::vector > pending; while(count < num && (uriListParser_ || !reservedGroups_.empty())) { @@ -488,6 +495,7 @@ void RequestGroupMan::fillRequestGroupFromReserver(DownloadEngine* e) configureRequestGroup(groupToAdd); groupToAdd->setRequestGroupMan(this); groupToAdd->setState(RequestGroup::STATE_ACTIVE); + ++numActive_; requestGroups_.push_back(groupToAdd->getGID(), groupToAdd); try { auto res = createInitialCommand(groupToAdd, e); @@ -965,4 +973,10 @@ void RequestGroupMan::initWrDiskCache() } } +void RequestGroupMan::decreaseNumActive() +{ + assert(numActive_ > 0); + --numActive_; +} + } // namespace aria2 diff --git a/src/RequestGroupMan.h b/src/RequestGroupMan.h index a9b8c4ecb0..fa08f18267 100644 --- a/src/RequestGroupMan.h +++ b/src/RequestGroupMan.h @@ -75,6 +75,13 @@ class RequestGroupMan { int maxSimultaneousDownloads_; + // The number of simultaneous active downloads, excluding seed only + // item if PREF_BT_DETACH_SEED_ONLY is true. We rely on this + // variable to maintain the number of concurrent downloads. If + // PREF_BT_DETACH_SEED_ONLY is false, this variable is equal to + // requestGroups_.size(). + size_t numActive_; + const Option* option_; std::shared_ptr serverStatMan_; @@ -376,6 +383,8 @@ class RequestGroupMan { { return openedFileCounter_; } + + void decreaseNumActive(); }; } // namespace aria2 diff --git a/src/prefs.cc b/src/prefs.cc index 4ceea78aef..a6de66ef9f 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -542,6 +542,7 @@ PrefPtr PREF_BT_EXCLUDE_TRACKER = makePref("bt-exclude-tracker"); // values: true | false PrefPtr PREF_BT_REMOVE_UNSELECTED_FILE = makePref("bt-remove-unselected-file"); +PrefPtr PREF_BT_DETACH_SEED_ONLY = makePref("bt-detach-seed-only"); /** * Metalink related preferences diff --git a/src/prefs.h b/src/prefs.h index 071e165158..33ab67ac47 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -478,6 +478,8 @@ extern PrefPtr PREF_BT_TRACKER; extern PrefPtr PREF_BT_EXCLUDE_TRACKER; // values: true | false extern PrefPtr PREF_BT_REMOVE_UNSELECTED_FILE; +// values: true |false +extern PrefPtr PREF_BT_DETACH_SEED_ONLY; /** * Metalink related preferences diff --git a/src/usage_text.h b/src/usage_text.h index d8d60b3177..2831b48eea 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -1000,3 +1000,14 @@ " using their metadata. This option pauses these\n" \ " subsequent downloads. This option is effective\n" \ " only when --enable-rpc=true is given.") +#define TEXT_BT_DETACH_SEED_ONLY \ + _(" --bt-detach-seed-only[=true|false]\n" \ + " Exclude seed only downloads when counting\n" \ + " concurrent active downloads (See -j option).\n" \ + " This means that if -j3 is given and this option\n" \ + " is turned on and 3 downloads are active and one\n" \ + " of those enters seed mode, then it is excluded\n" \ + " from active download count (thus it becomes 2),\n" \ + " and the next download waiting in queue gets\n" \ + " started. But be aware that seeding item is still\n" \ + " recognized as active download in RPC method.")