123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074 |
- /* <!-- copyright */
- /*
- * aria2 - The high speed download utility
- *
- * Copyright (C) 2006 Tatsuhiro Tsujikawa
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * In addition, as a special exception, the copyright holders give
- * permission to link the code of portions of this program with the
- * OpenSSL library under certain conditions as described in each
- * individual source file, and distribute linked combinations
- * including the two.
- * You must obey the GNU General Public License in all respects
- * for all of the code used other than OpenSSL. If you modify
- * file(s) with this exception, you may extend this exception to your
- * version of the file(s), but you are not obligated to do so. If you
- * do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source
- * files in the program, then also delete it here.
- */
- /* copyright --> */
- #include "RequestGroupMan.h"
- #include <unistd.h>
- #include <cstring>
- #include <iomanip>
- #include <sstream>
- #include <numeric>
- #include <algorithm>
- #include <utility>
- #include "BtProgressInfoFile.h"
- #include "RecoverableException.h"
- #include "RequestGroup.h"
- #include "LogFactory.h"
- #include "Logger.h"
- #include "DownloadEngine.h"
- #include "message.h"
- #include "a2functional.h"
- #include "DownloadResult.h"
- #include "DownloadContext.h"
- #include "ServerStatMan.h"
- #include "ServerStat.h"
- #include "SegmentMan.h"
- #include "FeedbackURISelector.h"
- #include "InorderURISelector.h"
- #include "AdaptiveURISelector.h"
- #include "Option.h"
- #include "prefs.h"
- #include "File.h"
- #include "util.h"
- #include "Command.h"
- #include "FileEntry.h"
- #include "fmt.h"
- #include "FileAllocationEntry.h"
- #include "CheckIntegrityEntry.h"
- #include "Segment.h"
- #include "DlAbortEx.h"
- #include "uri.h"
- #include "Signature.h"
- #include "OutputFile.h"
- #include "download_helper.h"
- #include "UriListParser.h"
- #include "SingletonHolder.h"
- #include "Notifier.h"
- #include "PeerStat.h"
- #include "WrDiskCache.h"
- #include "PieceStorage.h"
- #include "DiskAdaptor.h"
- #include "SimpleRandomizer.h"
- #include "array_fun.h"
- #include "OpenedFileCounter.h"
- #include "wallclock.h"
- #ifdef ENABLE_BITTORRENT
- #include "bittorrent_helper.h"
- #endif // ENABLE_BITTORRENT
- namespace aria2 {
- namespace {
- template <typename InputIterator>
- void appendReservedGroup(RequestGroupList& list, InputIterator first,
- InputIterator last)
- {
- for (; first != last; ++first) {
- list.push_back((*first)->getGID(), *first);
- }
- }
- } // namespace
- RequestGroupMan::RequestGroupMan(
- std::vector<std::shared_ptr<RequestGroup>> requestGroups,
- int maxConcurrentDownloads, const Option* option)
- : maxConcurrentDownloads_(maxConcurrentDownloads),
- optimizeConcurrentDownloads_(false),
- optimizeConcurrentDownloadsCoeffA_(5.),
- optimizeConcurrentDownloadsCoeffB_(25.),
- optimizationSpeed_(0),
- numActive_(0),
- option_(option),
- serverStatMan_(std::make_shared<ServerStatMan>()),
- maxOverallDownloadSpeedLimit_(
- option->getAsInt(PREF_MAX_OVERALL_DOWNLOAD_LIMIT)),
- maxOverallUploadSpeedLimit_(
- option->getAsInt(PREF_MAX_OVERALL_UPLOAD_LIMIT)),
- keepRunning_(option->getAsBool(PREF_ENABLE_RPC)),
- queueCheck_(true),
- removedErrorResult_(0),
- removedLastErrorResult_(error_code::FINISHED),
- maxDownloadResult_(option->getAsInt(PREF_MAX_DOWNLOAD_RESULT)),
- openedFileCounter_(std::make_shared<OpenedFileCounter>(
- this, option->getAsInt(PREF_BT_MAX_OPEN_FILES))),
- numStoppedTotal_(0)
- {
- setupOptimizeConcurrentDownloads();
- appendReservedGroup(reservedGroups_, requestGroups.begin(),
- requestGroups.end());
- }
- RequestGroupMan::~RequestGroupMan() { openedFileCounter_->deactivate(); }
- bool RequestGroupMan::setupOptimizeConcurrentDownloads(void)
- {
- optimizeConcurrentDownloads_ = option_->getAsBool(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS);
- if (optimizeConcurrentDownloads_) {
- if (option_->defined(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA)) {
- optimizeConcurrentDownloadsCoeffA_ = std::stod(option_->get(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFA));
- optimizeConcurrentDownloadsCoeffB_ = std::stod(option_->get(PREF_OPTIMIZE_CONCURRENT_DOWNLOADS_COEFFB));
- }
- }
- return optimizeConcurrentDownloads_;
- }
- bool RequestGroupMan::downloadFinished()
- {
- if (keepRunning_) {
- return false;
- }
- return requestGroups_.empty() && reservedGroups_.empty();
- }
- void RequestGroupMan::addRequestGroup(
- const std::shared_ptr<RequestGroup>& group)
- {
- ++numActive_;
- requestGroups_.push_back(group->getGID(), group);
- }
- void RequestGroupMan::addReservedGroup(
- const std::vector<std::shared_ptr<RequestGroup>>& groups)
- {
- requestQueueCheck();
- appendReservedGroup(reservedGroups_, groups.begin(), groups.end());
- }
- void RequestGroupMan::addReservedGroup(
- const std::shared_ptr<RequestGroup>& group)
- {
- requestQueueCheck();
- reservedGroups_.push_back(group->getGID(), group);
- }
- namespace {
- struct RequestGroupKeyFunc {
- a2_gid_t operator()(const std::shared_ptr<RequestGroup>& rg) const
- {
- return rg->getGID();
- }
- };
- } // namespace
- void RequestGroupMan::insertReservedGroup(
- size_t pos, const std::vector<std::shared_ptr<RequestGroup>>& groups)
- {
- requestQueueCheck();
- pos = std::min(reservedGroups_.size(), pos);
- reservedGroups_.insert(pos, RequestGroupKeyFunc(), groups.begin(),
- groups.end());
- }
- void RequestGroupMan::insertReservedGroup(
- size_t pos, const std::shared_ptr<RequestGroup>& group)
- {
- requestQueueCheck();
- pos = std::min(reservedGroups_.size(), pos);
- reservedGroups_.insert(pos, group->getGID(), group);
- }
- size_t RequestGroupMan::countRequestGroup() const
- {
- return requestGroups_.size();
- }
- std::shared_ptr<RequestGroup> RequestGroupMan::findGroup(a2_gid_t gid) const
- {
- std::shared_ptr<RequestGroup> rg = requestGroups_.get(gid);
- if (!rg) {
- rg = reservedGroups_.get(gid);
- }
- return rg;
- }
- size_t RequestGroupMan::changeReservedGroupPosition(a2_gid_t gid, int pos,
- OffsetMode how)
- {
- ssize_t dest = reservedGroups_.move(gid, pos, how);
- if (dest == -1) {
- throw DL_ABORT_EX(fmt("GID#%s not found in the waiting queue.",
- GroupId::toHex(gid).c_str()));
- }
- else {
- return dest;
- }
- }
- bool RequestGroupMan::removeReservedGroup(a2_gid_t gid)
- {
- return reservedGroups_.remove(gid);
- }
- namespace {
- void notifyDownloadEvent(DownloadEvent event,
- const std::shared_ptr<RequestGroup>& group)
- {
- // Check NULL to make unit test easier.
- if (SingletonHolder<Notifier>::instance()) {
- SingletonHolder<Notifier>::instance()->notifyDownloadEvent(event, group);
- }
- }
- } // namespace
- namespace {
- void executeStopHook(const std::shared_ptr<RequestGroup>& group,
- const Option* option, error_code::Value result)
- {
- PrefPtr hookPref = nullptr;
- if (!option->blank(PREF_ON_DOWNLOAD_STOP)) {
- hookPref = PREF_ON_DOWNLOAD_STOP;
- }
- if (result == error_code::FINISHED) {
- if (!option->blank(PREF_ON_DOWNLOAD_COMPLETE)) {
- hookPref = PREF_ON_DOWNLOAD_COMPLETE;
- }
- }
- else if (result != error_code::IN_PROGRESS && result != error_code::REMOVED) {
- if (!option->blank(PREF_ON_DOWNLOAD_ERROR)) {
- hookPref = PREF_ON_DOWNLOAD_ERROR;
- }
- }
- if (hookPref) {
- util::executeHookByOptName(group, option, hookPref);
- }
- if (result == error_code::FINISHED) {
- notifyDownloadEvent(EVENT_ON_DOWNLOAD_COMPLETE, group);
- }
- else if (result != error_code::IN_PROGRESS && result != error_code::REMOVED) {
- notifyDownloadEvent(EVENT_ON_DOWNLOAD_ERROR, group);
- }
- else {
- notifyDownloadEvent(EVENT_ON_DOWNLOAD_STOP, group);
- }
- }
- } // namespace
- namespace {
- class ProcessStoppedRequestGroup {
- private:
- DownloadEngine* e_;
- RequestGroupList& reservedGroups_;
- void saveSignature(const std::shared_ptr<RequestGroup>& group)
- {
- auto& sig = group->getDownloadContext()->getSignature();
- if (sig && !sig->getBody().empty()) {
- // filename of signature file is the path to download file followed by
- // ".sig".
- std::string signatureFile = group->getFirstFilePath() + ".sig";
- if (sig->save(signatureFile)) {
- A2_LOG_NOTICE(fmt(MSG_SIGNATURE_SAVED, signatureFile.c_str()));
- }
- else {
- A2_LOG_NOTICE(fmt(MSG_SIGNATURE_NOT_SAVED, signatureFile.c_str()));
- }
- }
- }
- // Collect statistics during download in PeerStats and update/register
- // ServerStatMan
- void collectStat(const std::shared_ptr<RequestGroup>& group)
- {
- if (group->getSegmentMan()) {
- bool singleConnection =
- group->getSegmentMan()->getPeerStats().size() == 1;
- const std::vector<std::shared_ptr<PeerStat>>& peerStats =
- group->getSegmentMan()->getFastestPeerStats();
- for (auto& stat : peerStats) {
- if (stat->getHostname().empty() || stat->getProtocol().empty()) {
- continue;
- }
- int speed = stat->getAvgDownloadSpeed();
- if (speed == 0)
- continue;
- std::shared_ptr<ServerStat> ss =
- e_->getRequestGroupMan()->getOrCreateServerStat(
- stat->getHostname(), stat->getProtocol());
- ss->increaseCounter();
- ss->updateDownloadSpeed(speed);
- if (singleConnection) {
- ss->updateSingleConnectionAvgSpeed(speed);
- }
- else {
- ss->updateMultiConnectionAvgSpeed(speed);
- }
- }
- }
- }
- public:
- ProcessStoppedRequestGroup(DownloadEngine* e,
- RequestGroupList& reservedGroups)
- : e_(e), reservedGroups_(reservedGroups)
- {
- }
- bool operator()(const RequestGroupList::value_type& group)
- {
- if (group->getNumCommand() == 0) {
- collectStat(group);
- const std::shared_ptr<DownloadContext>& dctx =
- group->getDownloadContext();
- if (!group->isSeedOnlyEnabled()) {
- e_->getRequestGroupMan()->decreaseNumActive();
- }
- // DownloadContext::resetDownloadStopTime() is only called when
- // download completed. If
- // DownloadContext::getDownloadStopTime().isZero() is true, then
- // there is a possibility that the download is error or
- // in-progress and resetDownloadStopTime() is not called. So
- // call it here.
- if (dctx->getDownloadStopTime().isZero()) {
- dctx->resetDownloadStopTime();
- }
- try {
- group->closeFile();
- if (group->isPauseRequested()) {
- A2_LOG_NOTICE(fmt(_("Download GID#%s paused"),
- GroupId::toHex(group->getGID()).c_str()));
- group->saveControlFile();
- }
- else if (group->downloadFinished() &&
- !group->getDownloadContext()->isChecksumVerificationNeeded()) {
- group->applyLastModifiedTimeToLocalFiles();
- group->reportDownloadFinished();
- if (group->allDownloadFinished() &&
- !group->getOption()->getAsBool(PREF_FORCE_SAVE)) {
- group->removeControlFile();
- saveSignature(group);
- }
- else {
- group->saveControlFile();
- }
- std::vector<std::shared_ptr<RequestGroup>> nextGroups;
- group->postDownloadProcessing(nextGroups);
- if (!nextGroups.empty()) {
- A2_LOG_DEBUG(fmt("Adding %lu RequestGroups as a result of"
- " PostDownloadHandler.",
- static_cast<unsigned long>(nextGroups.size())));
- e_->getRequestGroupMan()->insertReservedGroup(0, nextGroups);
- }
- #ifdef ENABLE_BITTORRENT
- // For in-memory download (e.g., Magnet URI), the
- // FileEntry::getPath() does not return actual file path, so
- // we don't remove it.
- if (group->getOption()->getAsBool(PREF_BT_REMOVE_UNSELECTED_FILE) &&
- !group->inMemoryDownload() && dctx->hasAttribute(CTX_ATTR_BT)) {
- A2_LOG_INFO(fmt(MSG_REMOVING_UNSELECTED_FILE,
- GroupId::toHex(group->getGID()).c_str()));
- const std::vector<std::shared_ptr<FileEntry>>& files =
- dctx->getFileEntries();
- for (auto& file : files) {
- if (!file->isRequested()) {
- if (File(file->getPath()).remove()) {
- A2_LOG_INFO(fmt(MSG_FILE_REMOVED, file->getPath().c_str()));
- }
- else {
- A2_LOG_INFO(
- fmt(MSG_FILE_COULD_NOT_REMOVED, file->getPath().c_str()));
- }
- }
- }
- }
- #endif // ENABLE_BITTORRENT
- }
- else {
- A2_LOG_NOTICE(
- fmt(_("Download GID#%s not complete: %s"),
- GroupId::toHex(group->getGID()).c_str(),
- group->getDownloadContext()->getBasePath().c_str()));
- group->saveControlFile();
- }
- }
- catch (RecoverableException& ex) {
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, ex);
- }
- if (group->isPauseRequested()) {
- group->setState(RequestGroup::STATE_WAITING);
- reservedGroups_.push_front(group->getGID(), group);
- group->releaseRuntimeResource(e_);
- group->setForceHaltRequested(false);
- util::executeHookByOptName(group, e_->getOption(),
- PREF_ON_DOWNLOAD_PAUSE);
- notifyDownloadEvent(EVENT_ON_DOWNLOAD_PAUSE, group);
- // TODO Should we have to prepend spend uris to remaining uris
- // in case PREF_REUSE_URI is disabled?
- }
- else {
- std::shared_ptr<DownloadResult> dr = group->createDownloadResult();
- e_->getRequestGroupMan()->addDownloadResult(dr);
- executeStopHook(group, e_->getOption(), dr->result);
- group->releaseRuntimeResource(e_);
- }
- return true;
- }
- else {
- return false;
- }
- }
- };
- } // namespace
- void RequestGroupMan::removeStoppedGroup(DownloadEngine* e)
- {
- size_t numPrev = requestGroups_.size();
- requestGroups_.remove_if(ProcessStoppedRequestGroup(e, reservedGroups_));
- size_t numRemoved = numPrev - requestGroups_.size();
- if (numRemoved > 0) {
- A2_LOG_DEBUG(fmt("%lu RequestGroup(s) deleted.",
- static_cast<unsigned long>(numRemoved)));
- }
- }
- void RequestGroupMan::configureRequestGroup(
- const std::shared_ptr<RequestGroup>& requestGroup) const
- {
- const std::string& uriSelectorValue =
- requestGroup->getOption()->get(PREF_URI_SELECTOR);
- if (uriSelectorValue == V_FEEDBACK) {
- requestGroup->setURISelector(
- make_unique<FeedbackURISelector>(serverStatMan_));
- }
- else if (uriSelectorValue == V_INORDER) {
- requestGroup->setURISelector(make_unique<InorderURISelector>());
- }
- else if (uriSelectorValue == V_ADAPTIVE) {
- requestGroup->setURISelector(
- make_unique<AdaptiveURISelector>(serverStatMan_, requestGroup.get()));
- }
- }
- namespace {
- std::vector<std::unique_ptr<Command>>
- createInitialCommand(const std::shared_ptr<RequestGroup>& requestGroup,
- DownloadEngine* e)
- {
- std::vector<std::unique_ptr<Command>> res;
- requestGroup->createInitialCommand(res, e);
- return res;
- }
- } // namespace
- void RequestGroupMan::fillRequestGroupFromReserver(DownloadEngine* e)
- {
- removeStoppedGroup(e);
- int maxConcurrentDownloads = optimizeConcurrentDownloads_ ? optimizeConcurrentDownloads() : maxConcurrentDownloads_;
- if (static_cast<size_t>(maxConcurrentDownloads) <= numActive_) {
- return;
- }
- int count = 0;
- int num = maxConcurrentDownloads - numActive_;
- std::vector<std::shared_ptr<RequestGroup>> pending;
- while (count < num && (uriListParser_ || !reservedGroups_.empty())) {
- if (uriListParser_ && reservedGroups_.empty()) {
- std::vector<std::shared_ptr<RequestGroup>> groups;
- // May throw exception
- bool ok = createRequestGroupFromUriListParser(groups, option_,
- uriListParser_.get());
- if (ok) {
- appendReservedGroup(reservedGroups_, groups.begin(), groups.end());
- }
- else {
- uriListParser_.reset();
- if (reservedGroups_.empty()) {
- break;
- }
- }
- }
- std::shared_ptr<RequestGroup> groupToAdd = *reservedGroups_.begin();
- reservedGroups_.pop_front();
- if ((keepRunning_ && groupToAdd->isPauseRequested()) ||
- !groupToAdd->isDependencyResolved()) {
- pending.push_back(groupToAdd);
- continue;
- }
- // Drop pieceStorage here because paused download holds its
- // reference.
- groupToAdd->dropPieceStorage();
- configureRequestGroup(groupToAdd);
- groupToAdd->setRequestGroupMan(this);
- groupToAdd->setState(RequestGroup::STATE_ACTIVE);
- ++numActive_;
- requestGroups_.push_back(groupToAdd->getGID(), groupToAdd);
- try {
- auto res = createInitialCommand(groupToAdd, e);
- ++count;
- if (res.empty()) {
- requestQueueCheck();
- }
- else {
- e->addCommand(std::move(res));
- }
- }
- catch (RecoverableException& ex) {
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, ex);
- A2_LOG_DEBUG("Deleting temporal commands.");
- groupToAdd->setLastErrorCode(ex.getErrorCode(), ex.what());
- // We add groupToAdd to e later in order to it is processed in
- // removeStoppedGroup().
- requestQueueCheck();
- }
- util::executeHookByOptName(groupToAdd, e->getOption(),
- PREF_ON_DOWNLOAD_START);
- notifyDownloadEvent(EVENT_ON_DOWNLOAD_START, groupToAdd);
- }
- if (!pending.empty()) {
- reservedGroups_.insert(reservedGroups_.begin(), RequestGroupKeyFunc(),
- pending.begin(), pending.end());
- }
- if (count > 0) {
- e->setNoWait(true);
- e->setRefreshInterval(std::chrono::milliseconds(0));
- A2_LOG_DEBUG(fmt("%d RequestGroup(s) added.", count));
- }
- }
- void RequestGroupMan::save()
- {
- for (auto& rg : requestGroups_) {
- if (rg->allDownloadFinished() &&
- !rg->getDownloadContext()->isChecksumVerificationNeeded() &&
- !rg->getOption()->getAsBool(PREF_FORCE_SAVE)) {
- rg->removeControlFile();
- }
- else {
- try {
- rg->saveControlFile();
- }
- catch (RecoverableException& e) {
- A2_LOG_ERROR_EX(EX_EXCEPTION_CAUGHT, e);
- }
- }
- }
- }
- void RequestGroupMan::closeFile()
- {
- for (auto& elem : requestGroups_) {
- elem->closeFile();
- }
- }
- RequestGroupMan::DownloadStat RequestGroupMan::getDownloadStat() const
- {
- int finished = 0;
- int error = removedErrorResult_;
- int inprogress = 0;
- int removed = 0;
- error_code::Value lastError = removedLastErrorResult_;
- for (auto& dr : downloadResults_) {
- if (dr->belongsTo != 0) {
- continue;
- }
- if (dr->result == error_code::FINISHED) {
- ++finished;
- }
- else if (dr->result == error_code::IN_PROGRESS) {
- ++inprogress;
- }
- else if (dr->result == error_code::REMOVED) {
- ++removed;
- }
- else {
- ++error;
- lastError = dr->result;
- }
- }
- return DownloadStat(error, inprogress, reservedGroups_.size(), lastError);
- }
- enum DownloadResultStatus {
- A2_STATUS_OK,
- A2_STATUS_INPR,
- A2_STATUS_RM,
- A2_STATUS_ERR
- };
- namespace {
- const char* getStatusStr(DownloadResultStatus status, bool useColor)
- {
- // status string is formatted in 4 characters wide.
- switch (status) {
- case (A2_STATUS_OK):
- if (useColor) {
- return "\033[1;32mOK\033[0m ";
- }
- else {
- return "OK ";
- }
- case (A2_STATUS_INPR):
- if (useColor) {
- return "\033[1;34mINPR\033[0m";
- }
- else {
- return "INPR";
- }
- case (A2_STATUS_RM):
- if (useColor) {
- return "\033[1mRM\033[0m ";
- }
- else {
- return "RM ";
- }
- case (A2_STATUS_ERR):
- if (useColor) {
- return "\033[1;31mERR\033[0m ";
- }
- else {
- return "ERR ";
- }
- default:
- return "";
- }
- }
- } // namespace
- void RequestGroupMan::showDownloadResults(OutputFile& o, bool full) const
- {
- int pathRowSize = 55;
- // Download Results:
- // idx|stat|path/length
- // ===+====+=======================================================================
- o.printf("\n%s"
- "\ngid |stat|avg speed |",
- _("Download Results:"));
- if (full) {
- o.write(" %|path/URI"
- "\n======+====+===========+===+");
- pathRowSize -= 4;
- }
- else {
- o.write("path/URI"
- "\n======+====+===========+");
- }
- std::string line(pathRowSize, '=');
- o.printf("%s\n", line.c_str());
- bool useColor = o.supportsColor() && option_->getAsBool(PREF_ENABLE_COLOR);
- int ok = 0;
- int err = 0;
- int inpr = 0;
- int rm = 0;
- for (auto& dr : downloadResults_) {
- if (dr->belongsTo != 0) {
- continue;
- }
- const char* status;
- switch (dr->result) {
- case error_code::FINISHED:
- status = getStatusStr(A2_STATUS_OK, useColor);
- ++ok;
- break;
- case error_code::IN_PROGRESS:
- status = getStatusStr(A2_STATUS_INPR, useColor);
- ++inpr;
- break;
- case error_code::REMOVED:
- status = getStatusStr(A2_STATUS_RM, useColor);
- ++rm;
- break;
- default:
- status = getStatusStr(A2_STATUS_ERR, useColor);
- ++err;
- }
- if (full) {
- formatDownloadResultFull(o, status, dr);
- }
- else {
- o.write(formatDownloadResult(status, dr).c_str());
- o.write("\n");
- }
- }
- if (ok > 0 || err > 0 || inpr > 0 || rm > 0) {
- o.printf("\n%s\n", _("Status Legend:"));
- if (ok > 0) {
- o.write(_("(OK):download completed."));
- }
- if (err > 0) {
- o.write(_("(ERR):error occurred."));
- }
- if (inpr > 0) {
- o.write(_("(INPR):download in-progress."));
- }
- if (rm > 0) {
- o.write(_("(RM):download removed."));
- }
- o.write("\n");
- }
- }
- namespace {
- void formatDownloadResultCommon(
- std::ostream& o, const char* status,
- const std::shared_ptr<DownloadResult>& downloadResult)
- {
- o << std::setw(3) << downloadResult->gid->toAbbrevHex() << "|" << std::setw(4)
- << status << "|";
- if (downloadResult->sessionTime.count() > 0) {
- o << std::setw(8)
- << util::abbrevSize(downloadResult->sessionDownloadLength * 1000 /
- downloadResult->sessionTime.count()) << "B/s";
- }
- else {
- o << std::setw(11);
- o << "n/a";
- }
- o << "|";
- }
- } // namespace
- void RequestGroupMan::formatDownloadResultFull(
- OutputFile& out, const char* status,
- const std::shared_ptr<DownloadResult>& downloadResult) const
- {
- BitfieldMan bt(downloadResult->pieceLength, downloadResult->totalLength);
- bt.setBitfield(
- reinterpret_cast<const unsigned char*>(downloadResult->bitfield.data()),
- downloadResult->bitfield.size());
- bool head = true;
- const std::vector<std::shared_ptr<FileEntry>>& fileEntries =
- downloadResult->fileEntries;
- for (auto& f : fileEntries) {
- if (!f->isRequested()) {
- continue;
- }
- std::stringstream o;
- if (head) {
- formatDownloadResultCommon(o, status, downloadResult);
- head = false;
- }
- else {
- o << " | | |";
- }
- if (f->getLength() == 0 || downloadResult->bitfield.empty()) {
- o << " -|";
- }
- else {
- int64_t completedLength =
- bt.getOffsetCompletedLength(f->getOffset(), f->getLength());
- o << std::setw(3) << 100 * completedLength / f->getLength() << "|";
- }
- writeFilePath(o, f, downloadResult->inMemoryDownload);
- o << "\n";
- out.write(o.str().c_str());
- }
- if (head) {
- std::stringstream o;
- formatDownloadResultCommon(o, status, downloadResult);
- o << " -|n/a\n";
- out.write(o.str().c_str());
- }
- }
- std::string RequestGroupMan::formatDownloadResult(
- const char* status,
- const std::shared_ptr<DownloadResult>& downloadResult) const
- {
- std::stringstream o;
- formatDownloadResultCommon(o, status, downloadResult);
- const std::vector<std::shared_ptr<FileEntry>>& fileEntries =
- downloadResult->fileEntries;
- writeFilePath(fileEntries.begin(), fileEntries.end(), o,
- downloadResult->inMemoryDownload);
- return o.str();
- }
- namespace {
- template <typename StringInputIterator, typename FileEntryInputIterator>
- bool sameFilePathExists(StringInputIterator sfirst, StringInputIterator slast,
- FileEntryInputIterator ffirst,
- FileEntryInputIterator flast)
- {
- for (; ffirst != flast; ++ffirst) {
- if (std::binary_search(sfirst, slast, (*ffirst)->getPath())) {
- return true;
- }
- }
- return false;
- }
- } // namespace
- bool RequestGroupMan::isSameFileBeingDownloaded(
- RequestGroup* requestGroup) const
- {
- // TODO it may be good to use dedicated method rather than use
- // isPreLocalFileCheckEnabled
- if (!requestGroup->isPreLocalFileCheckEnabled()) {
- return false;
- }
- std::vector<std::string> files;
- for (auto& rg : requestGroups_) {
- if (rg.get() != requestGroup) {
- const std::vector<std::shared_ptr<FileEntry>>& entries =
- rg->getDownloadContext()->getFileEntries();
- std::transform(entries.begin(), entries.end(), std::back_inserter(files),
- std::mem_fn(&FileEntry::getPath));
- }
- }
- std::sort(files.begin(), files.end());
- const std::vector<std::shared_ptr<FileEntry>>& entries =
- requestGroup->getDownloadContext()->getFileEntries();
- return sameFilePathExists(files.begin(), files.end(), entries.begin(),
- entries.end());
- }
- void RequestGroupMan::halt()
- {
- for (auto& elem : requestGroups_) {
- elem->setHaltRequested(true);
- }
- }
- void RequestGroupMan::forceHalt()
- {
- for (auto& elem : requestGroups_) {
- elem->setForceHaltRequested(true);
- }
- }
- TransferStat RequestGroupMan::calculateStat()
- {
- // TODO Currently, all time upload length is not set.
- return netStat_.toTransferStat();
- }
- std::shared_ptr<DownloadResult>
- RequestGroupMan::findDownloadResult(a2_gid_t gid) const
- {
- return downloadResults_.get(gid);
- }
- bool RequestGroupMan::removeDownloadResult(a2_gid_t gid)
- {
- return downloadResults_.remove(gid);
- }
- void RequestGroupMan::addDownloadResult(
- const std::shared_ptr<DownloadResult>& dr)
- {
- ++numStoppedTotal_;
- bool rv = downloadResults_.push_back(dr->gid->getNumericId(), dr);
- assert(rv);
- while (downloadResults_.size() > maxDownloadResult_) {
- DownloadResultList::iterator i = downloadResults_.begin();
- // Save last encountered error code so that we can report it
- // later.
- const std::shared_ptr<DownloadResult>& dr = *i;
- if (dr->belongsTo == 0 && dr->result != error_code::FINISHED) {
- removedLastErrorResult_ = dr->result;
- ++removedErrorResult_;
- }
- downloadResults_.pop_front();
- }
- }
- void RequestGroupMan::purgeDownloadResult() { downloadResults_.clear(); }
- std::shared_ptr<ServerStat>
- RequestGroupMan::findServerStat(const std::string& hostname,
- const std::string& protocol) const
- {
- return serverStatMan_->find(hostname, protocol);
- }
- std::shared_ptr<ServerStat>
- RequestGroupMan::getOrCreateServerStat(const std::string& hostname,
- const std::string& protocol)
- {
- std::shared_ptr<ServerStat> ss = findServerStat(hostname, protocol);
- if (!ss) {
- ss = std::make_shared<ServerStat>(hostname, protocol);
- addServerStat(ss);
- }
- return ss;
- }
- bool RequestGroupMan::addServerStat(
- const std::shared_ptr<ServerStat>& serverStat)
- {
- return serverStatMan_->add(serverStat);
- }
- bool RequestGroupMan::loadServerStat(const std::string& filename)
- {
- return serverStatMan_->load(filename);
- }
- bool RequestGroupMan::saveServerStat(const std::string& filename) const
- {
- return serverStatMan_->save(filename);
- }
- void RequestGroupMan::removeStaleServerStat(const std::chrono::seconds& timeout)
- {
- serverStatMan_->removeStaleServerStat(timeout);
- }
- bool RequestGroupMan::doesOverallDownloadSpeedExceed()
- {
- return maxOverallDownloadSpeedLimit_ > 0 &&
- maxOverallDownloadSpeedLimit_ < netStat_.calculateDownloadSpeed();
- }
- bool RequestGroupMan::doesOverallUploadSpeedExceed()
- {
- return maxOverallUploadSpeedLimit_ > 0 &&
- maxOverallUploadSpeedLimit_ < netStat_.calculateUploadSpeed();
- }
- void RequestGroupMan::getUsedHosts(
- std::vector<std::pair<size_t, std::string>>& usedHosts)
- {
- // vector of tuple which consists of use count, -download speed,
- // hostname. We want to sort by least used and faster download
- // speed. We use -download speed so that we can sort them using
- // operator<().
- std::vector<std::tuple<size_t, int, std::string>> tempHosts;
- for (const auto& rg : requestGroups_) {
- const auto& inFlightReqs =
- rg->getDownloadContext()->getFirstFileEntry()->getInFlightRequests();
- for (const auto& req : inFlightReqs) {
- uri_split_result us;
- if (uri_split(&us, req->getUri().c_str()) == 0) {
- std::string host =
- uri::getFieldString(us, USR_HOST, req->getUri().c_str());
- auto k = tempHosts.begin();
- auto eok = tempHosts.end();
- for (; k != eok; ++k) {
- if (std::get<2>(*k) == host) {
- ++std::get<0>(*k);
- break;
- }
- }
- if (k == eok) {
- std::string protocol =
- uri::getFieldString(us, USR_SCHEME, req->getUri().c_str());
- auto ss = findServerStat(host, protocol);
- int invDlSpeed = (ss && ss->isOK())
- ? -(static_cast<int>(ss->getDownloadSpeed()))
- : 0;
- tempHosts.emplace_back(1, invDlSpeed, host);
- }
- }
- }
- }
- std::sort(tempHosts.begin(), tempHosts.end());
- std::transform(tempHosts.begin(), tempHosts.end(),
- std::back_inserter(usedHosts),
- [](const std::tuple<size_t, int, std::string>& x) {
- return std::make_pair(std::get<0>(x), std::get<2>(x));
- });
- }
- void RequestGroupMan::setUriListParser(
- const std::shared_ptr<UriListParser>& uriListParser)
- {
- uriListParser_ = uriListParser;
- }
- void RequestGroupMan::initWrDiskCache()
- {
- assert(!wrDiskCache_);
- size_t limit = option_->getAsInt(PREF_DISK_CACHE);
- if (limit > 0) {
- wrDiskCache_ = make_unique<WrDiskCache>(limit);
- }
- }
- void RequestGroupMan::decreaseNumActive()
- {
- assert(numActive_ > 0);
- --numActive_;
- }
- int RequestGroupMan::optimizeConcurrentDownloads()
- {
- // gauge the current speed
- int currentSpeed = getNetStat().calculateDownloadSpeed();
- const auto& now = global::wallclock();
- if (currentSpeed >= optimizationSpeed_) {
- optimizationSpeed_ = currentSpeed;
- optimizationSpeedTimer_ = now;
- } else if (std::chrono::duration_cast<std::chrono::seconds>(optimizationSpeedTimer_.difference(now)) >= 5_s) {
- // we keep using the reference speed for minimum 5 seconds so reset the timer
- optimizationSpeedTimer_ = now;
- // keep the reference speed as long as the speed tends to augment or to maintain itself within 10%
- if (currentSpeed >= 1.1 * getNetStat().calculateNewestDownloadSpeed(5)) {
- // else assume a possible congestion and record a new optimization speed by dichotomy
- optimizationSpeed_ = (optimizationSpeed_ + currentSpeed)/2.;
- }
- }
- if (optimizationSpeed_ <= 0) {
- return 1;
- }
- // apply the rule
- if ((maxOverallDownloadSpeedLimit_ > 0) && (optimizationSpeed_ > maxOverallDownloadSpeedLimit_)) {
- optimizationSpeed_ = maxOverallDownloadSpeedLimit_;
- }
- int maxConcurrentDownloads = ceil(
- optimizeConcurrentDownloadsCoeffA_
- + optimizeConcurrentDownloadsCoeffB_ * log10(optimizationSpeed_ * 8. / 1000000.)
- );
- // bring the value in bound between 1 and the defined maximum
- maxConcurrentDownloads = std::min(std::max(1, maxConcurrentDownloads), maxConcurrentDownloads_);
- A2_LOG_DEBUG
- (fmt("Max concurrent downloads optimized at %d (%lu currently active) "
- "[optimization speed %sB/s, current speed %sB/s]",
- maxConcurrentDownloads, numActive_, util::abbrevSize(optimizationSpeed_).c_str(),
- util::abbrevSize(currentSpeed).c_str()));
- return maxConcurrentDownloads;
- }
- } // namespace aria2
|