mirror of https://github.com/aria2/aria2
Restart active download to apply previously not applicable options
Previously, we categorized options that can be used in aria2.changeOption RPC method into 2 categories. The options in one category can be applied on the fly, meaning that download continues to be active while applying options. Another category includes options which are only applicable when downloads are waiting or paused. In this change, when active download is ordered to change options which only applicable in waiting or paused state, it is now paused, and then automatically restarted. Although we have limited number of download concurrency, the pause and restart is done atomically, and the download is inserted at the front of the queue, it is picked up immediately if the concurrency regulation allows.dynamic-select-file
parent
e174b90ff2
commit
8897d7ec70
|
@ -190,4 +190,10 @@ void Option::setParent(const std::shared_ptr<Option>& parent)
|
||||||
|
|
||||||
const std::shared_ptr<Option>& Option::getParent() const { return parent_; }
|
const std::shared_ptr<Option>& Option::getParent() const { return parent_; }
|
||||||
|
|
||||||
|
bool Option::emptyLocal() const
|
||||||
|
{
|
||||||
|
size_t dst;
|
||||||
|
return !bitfield::getFirstSetBitIndex(dst, use_, use_.size() * 8);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -92,6 +92,8 @@ public:
|
||||||
// Sets parent Option object for this object.
|
// Sets parent Option object for this object.
|
||||||
void setParent(const std::shared_ptr<Option>& parent);
|
void setParent(const std::shared_ptr<Option>& parent);
|
||||||
const std::shared_ptr<Option>& getParent() const;
|
const std::shared_ptr<Option>& getParent() const;
|
||||||
|
// Returns true if there is no option stored.
|
||||||
|
bool emptyLocal() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -957,6 +957,8 @@ void RequestGroup::setForceHaltRequested(bool f, HaltReason haltReason)
|
||||||
|
|
||||||
void RequestGroup::setPauseRequested(bool f) { pauseRequested_ = f; }
|
void RequestGroup::setPauseRequested(bool f) { pauseRequested_ = f; }
|
||||||
|
|
||||||
|
void RequestGroup::setRestartRequested(bool f) { restartRequested_ = f; }
|
||||||
|
|
||||||
void RequestGroup::releaseRuntimeResource(DownloadEngine* e)
|
void RequestGroup::releaseRuntimeResource(DownloadEngine* e)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_BITTORRENT
|
#ifdef ENABLE_BITTORRENT
|
||||||
|
@ -1279,4 +1281,9 @@ bool RequestGroup::isSeeder() const
|
||||||
#endif // !ENABLE_BITTORRENT
|
#endif // !ENABLE_BITTORRENT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RequestGroup::setPendingOption(std::shared_ptr<Option> option)
|
||||||
|
{
|
||||||
|
pendingOption_ = std::move(option);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -96,6 +96,9 @@ private:
|
||||||
|
|
||||||
std::shared_ptr<Option> option_;
|
std::shared_ptr<Option> option_;
|
||||||
|
|
||||||
|
// options applied on restart
|
||||||
|
std::shared_ptr<Option> pendingOption_;
|
||||||
|
|
||||||
std::shared_ptr<SegmentMan> segmentMan_;
|
std::shared_ptr<SegmentMan> segmentMan_;
|
||||||
|
|
||||||
std::shared_ptr<DownloadContext> downloadContext_;
|
std::shared_ptr<DownloadContext> downloadContext_;
|
||||||
|
@ -178,6 +181,11 @@ private:
|
||||||
|
|
||||||
bool pauseRequested_;
|
bool pauseRequested_;
|
||||||
|
|
||||||
|
// restartRequested_ indicates that this download should be
|
||||||
|
// restarted. Usually, it is used with pauseRequested_ to stop
|
||||||
|
// download first.
|
||||||
|
bool restartRequested_;
|
||||||
|
|
||||||
// This flag just indicates that the downloaded file is not saved disk but
|
// This flag just indicates that the downloaded file is not saved disk but
|
||||||
// just sits in memory.
|
// just sits in memory.
|
||||||
bool inMemoryDownload_;
|
bool inMemoryDownload_;
|
||||||
|
@ -345,6 +353,10 @@ public:
|
||||||
|
|
||||||
bool isPauseRequested() const { return pauseRequested_; }
|
bool isPauseRequested() const { return pauseRequested_; }
|
||||||
|
|
||||||
|
void setRestartRequested(bool f);
|
||||||
|
|
||||||
|
bool isRestartRequested() const { return restartRequested_; }
|
||||||
|
|
||||||
void dependsOn(const std::shared_ptr<Dependency>& dep);
|
void dependsOn(const std::shared_ptr<Dependency>& dep);
|
||||||
|
|
||||||
bool isDependencyResolved();
|
bool isDependencyResolved();
|
||||||
|
@ -500,6 +512,12 @@ public:
|
||||||
|
|
||||||
// Returns true if this download is now seeding.
|
// Returns true if this download is now seeding.
|
||||||
bool isSeeder() const;
|
bool isSeeder() const;
|
||||||
|
|
||||||
|
void setPendingOption(std::shared_ptr<Option> option);
|
||||||
|
const std::shared_ptr<Option>& getPendingOption() const
|
||||||
|
{
|
||||||
|
return pendingOption_;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
#include "array_fun.h"
|
#include "array_fun.h"
|
||||||
#include "OpenedFileCounter.h"
|
#include "OpenedFileCounter.h"
|
||||||
#include "wallclock.h"
|
#include "wallclock.h"
|
||||||
|
#include "RpcMethodImpl.h"
|
||||||
#ifdef ENABLE_BITTORRENT
|
#ifdef ENABLE_BITTORRENT
|
||||||
#include "bittorrent_helper.h"
|
#include "bittorrent_helper.h"
|
||||||
#endif // ENABLE_BITTORRENT
|
#endif // ENABLE_BITTORRENT
|
||||||
|
@ -369,8 +370,10 @@ public:
|
||||||
try {
|
try {
|
||||||
group->closeFile();
|
group->closeFile();
|
||||||
if (group->isPauseRequested()) {
|
if (group->isPauseRequested()) {
|
||||||
A2_LOG_NOTICE(fmt(_("Download GID#%s paused"),
|
if (!group->isRestartRequested()) {
|
||||||
GroupId::toHex(group->getGID()).c_str()));
|
A2_LOG_NOTICE(fmt(_("Download GID#%s paused"),
|
||||||
|
GroupId::toHex(group->getGID()).c_str()));
|
||||||
|
}
|
||||||
group->saveControlFile();
|
group->saveControlFile();
|
||||||
}
|
}
|
||||||
else if (group->downloadFinished() &&
|
else if (group->downloadFinished() &&
|
||||||
|
@ -433,9 +436,20 @@ public:
|
||||||
reservedGroups_.push_front(group->getGID(), group);
|
reservedGroups_.push_front(group->getGID(), group);
|
||||||
group->releaseRuntimeResource(e_);
|
group->releaseRuntimeResource(e_);
|
||||||
group->setForceHaltRequested(false);
|
group->setForceHaltRequested(false);
|
||||||
util::executeHookByOptName(group, e_->getOption(),
|
|
||||||
PREF_ON_DOWNLOAD_PAUSE);
|
auto pendingOption = group->getPendingOption();
|
||||||
notifyDownloadEvent(EVENT_ON_DOWNLOAD_PAUSE, group);
|
if (pendingOption) {
|
||||||
|
changeOption(group, *pendingOption, e_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group->isRestartRequested()) {
|
||||||
|
group->setPauseRequested(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
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
|
// TODO Should we have to prepend spend uris to remaining uris
|
||||||
// in case PREF_REUSE_URI is disabled?
|
// in case PREF_REUSE_URI is disabled?
|
||||||
}
|
}
|
||||||
|
@ -445,6 +459,10 @@ public:
|
||||||
executeStopHook(group, e_->getOption(), dr->result);
|
executeStopHook(group, e_->getOption(), dr->result);
|
||||||
group->releaseRuntimeResource(e_);
|
group->releaseRuntimeResource(e_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group->setRestartRequested(false);
|
||||||
|
group->setPendingOption(nullptr);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -147,12 +147,53 @@ void RpcMethod::gatherRequestOption(Option* option, const Dict* optionsDict)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RpcMethod::gatherChangeableOption(Option* option, const Dict* optionsDict)
|
void RpcMethod::gatherChangeableOption(Option* option, Option* pendingOption,
|
||||||
|
const Dict* optionsDict)
|
||||||
{
|
{
|
||||||
if (optionsDict) {
|
if (!optionsDict) {
|
||||||
gatherOption(optionsDict->begin(), optionsDict->end(),
|
return;
|
||||||
std::mem_fn(&OptionHandler::getChangeOption), option,
|
}
|
||||||
optionParser_);
|
|
||||||
|
auto first = optionsDict->begin();
|
||||||
|
auto last = optionsDict->end();
|
||||||
|
|
||||||
|
for (; first != last; ++first) {
|
||||||
|
const auto& optionName = (*first).first;
|
||||||
|
auto pref = option::k2p(optionName);
|
||||||
|
auto handler = optionParser_->find(pref);
|
||||||
|
if (!handler) {
|
||||||
|
// Just ignore the unacceptable options in this context.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Option* dst = nullptr;
|
||||||
|
if (handler->getChangeOption()) {
|
||||||
|
dst = option;
|
||||||
|
}
|
||||||
|
else if (handler->getChangeOptionForReserved()) {
|
||||||
|
dst = pendingOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dst) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto opval = downcast<String>((*first).second);
|
||||||
|
if (opval) {
|
||||||
|
handler->parse(*dst, opval->s());
|
||||||
|
}
|
||||||
|
else if (handler->getCumulative()) {
|
||||||
|
// header and index-out option can take array as value
|
||||||
|
const auto oplist = downcast<List>((*first).second);
|
||||||
|
if (oplist) {
|
||||||
|
for (auto& elem : *oplist) {
|
||||||
|
const auto opval = downcast<String>(elem);
|
||||||
|
if (opval) {
|
||||||
|
handler->parse(*dst, opval->s());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,8 @@ protected:
|
||||||
|
|
||||||
void gatherRequestOption(Option* option, const Dict* optionsDict);
|
void gatherRequestOption(Option* option, const Dict* optionsDict);
|
||||||
|
|
||||||
void gatherChangeableOption(Option* option, const Dict* optionDict);
|
void gatherChangeableOption(Option* option, Option* pendingOption,
|
||||||
|
const Dict* optionDict);
|
||||||
|
|
||||||
void gatherChangeableOptionForReserved(Option* option,
|
void gatherChangeableOptionForReserved(Option* option,
|
||||||
const Dict* optionsDict);
|
const Dict* optionsDict);
|
||||||
|
|
|
@ -1113,10 +1113,22 @@ std::unique_ptr<ValueBase> ChangeOptionRpcMethod::process(const RpcRequest& req,
|
||||||
|
|
||||||
a2_gid_t gid = str2Gid(gidParam);
|
a2_gid_t gid = str2Gid(gidParam);
|
||||||
auto group = e->getRequestGroupMan()->findGroup(gid);
|
auto group = e->getRequestGroupMan()->findGroup(gid);
|
||||||
Option option;
|
|
||||||
if (group) {
|
if (group) {
|
||||||
|
Option option;
|
||||||
|
std::shared_ptr<Option> pendingOption;
|
||||||
if (group->getState() == RequestGroup::STATE_ACTIVE) {
|
if (group->getState() == RequestGroup::STATE_ACTIVE) {
|
||||||
gatherChangeableOption(&option, optsParam);
|
pendingOption = std::make_shared<Option>();
|
||||||
|
gatherChangeableOption(&option, pendingOption.get(), optsParam);
|
||||||
|
if (!pendingOption->emptyLocal()) {
|
||||||
|
group->setPendingOption(pendingOption);
|
||||||
|
// pauseRequestGroup() may fail if group has been told to
|
||||||
|
// stop/pause already. In that case, we can still apply the
|
||||||
|
// pending options on pause.
|
||||||
|
if (pauseRequestGroup(group, false, false)) {
|
||||||
|
group->setRestartRequested(true);
|
||||||
|
e->setRefreshInterval(std::chrono::milliseconds(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
gatherChangeableOptionForReserved(&option, optsParam);
|
gatherChangeableOptionForReserved(&option, optsParam);
|
||||||
|
|
|
@ -143,8 +143,7 @@ size_t countSetBitSlow(const Array& bitfield, size_t nbits)
|
||||||
void flipBit(unsigned char* data, size_t length, size_t bitIndex);
|
void flipBit(unsigned char* data, size_t length, size_t bitIndex);
|
||||||
|
|
||||||
// Stores first set bit index of bitfield to index. bitfield contains
|
// Stores first set bit index of bitfield to index. bitfield contains
|
||||||
// nbits. Returns true if missing bit index is found. Otherwise
|
// nbits. Returns true if set bit is found. Otherwise returns false.
|
||||||
// returns false.
|
|
||||||
template <typename Array>
|
template <typename Array>
|
||||||
bool getFirstSetBitIndex(size_t& index, const Array& bitfield, size_t nbits)
|
bool getFirstSetBitIndex(size_t& index, const Array& bitfield, size_t nbits)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue