summaryrefslogtreecommitdiffstats
path: root/api/logic/FolderInstanceProvider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'api/logic/FolderInstanceProvider.cpp')
-rw-r--r--api/logic/FolderInstanceProvider.cpp122
1 files changed, 98 insertions, 24 deletions
diff --git a/api/logic/FolderInstanceProvider.cpp b/api/logic/FolderInstanceProvider.cpp
index 4893efd8..e0b578e8 100644
--- a/api/logic/FolderInstanceProvider.cpp
+++ b/api/logic/FolderInstanceProvider.cpp
@@ -12,6 +12,7 @@
#include <QJsonObject>
#include <QJsonArray>
#include <QUuid>
+#include <QTimer>
const static int GROUP_FILE_FORMAT_VERSION = 1;
@@ -110,24 +111,6 @@ InstancePtr FolderInstanceProvider::loadInstance(const InstanceId& id)
return inst;
}
-#include "InstanceImportTask.h"
-Task * FolderInstanceProvider::zipImportTask(const QUrl sourceUrl, const QString& instName, const QString& instGroup, const QString& instIcon)
-{
- return new InstanceImportTask(m_globalSettings, sourceUrl, this, instName, instIcon, instGroup);
-}
-
-#include "InstanceCreationTask.h"
-Task * FolderInstanceProvider::creationTask(BaseVersionPtr version, const QString& instName, const QString& instGroup, const QString& instIcon)
-{
- return new InstanceCreationTask(m_globalSettings, this, version, instName, instIcon, instGroup);
-}
-
-#include "InstanceCopyTask.h"
-Task * FolderInstanceProvider::copyTask(const InstancePtr& oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves)
-{
- return new InstanceCopyTask(m_globalSettings, this, oldInstance, instName, instIcon, instGroup, copySaves);
-}
-
void FolderInstanceProvider::saveGroupList()
{
WatchLock foo(m_watcher, m_instDir);
@@ -310,12 +293,65 @@ void FolderInstanceProvider::on_InstFolderChanged(const Setting &setting, QVaria
emit instancesChanged();
}
}
+
+template <typename T>
+static void clamp(T& current, T min, T max)
+{
+ if (current < min)
+ {
+ current = min;
+ }
+ else if(current > max)
+ {
+ current = max;
+ }
+}
+
+// List of numbers from min to max. Next is exponent times bigger than previous.
+class ExponentialSeries
+{
+public:
+ ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
+ {
+ m_current = m_min = min;
+ m_max = max;
+ m_exponent = exponent;
+ }
+ void reset()
+ {
+ m_current = m_min;
+ }
+ unsigned operator()()
+ {
+ unsigned retval = m_current;
+ m_current *= m_exponent;
+ clamp(m_current, m_min, m_max);
+ return retval;
+ }
+ unsigned m_current;
+ unsigned m_min;
+ unsigned m_max;
+ unsigned m_exponent;
+};
+
/*
+ * WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
+ * Basically, it starts messing things up while MultiMC is extracting/creating instances
+ * and causes that horrible failure that is NTFS to lock files in place because they are open.
+ */
class FolderInstanceStaging : public Task
{
-
+Q_OBJECT
+ const unsigned minBackoff = 1;
+ const unsigned maxBackoff = 16;
public:
- FolderInstanceStaging(FolderInstanceProvider * parent, Task * child, const QString& instanceName, const QString& groupName)
+ FolderInstanceStaging (
+ FolderInstanceProvider * parent,
+ Task * child,
+ const QString & stagingPath,
+ const QString& instanceName,
+ const QString& groupName )
+ : backoff(minBackoff, maxBackoff)
{
m_parent = parent;
m_child.reset(child);
@@ -325,22 +361,34 @@ public:
connect(child, &Task::progress, this, &FolderInstanceStaging::setProgress);
m_instanceName = instanceName;
m_groupName = groupName;
+ m_stagingPath = stagingPath;
+ m_backoffTimer.setSingleShot(true);
+ connect(&m_backoffTimer, &QTimer::timeout, this, &FolderInstanceStaging::childSucceded);
}
protected:
virtual void executeTask() override
{
- m_stagingPath = m_parent->getStagedInstancePath();
m_child->start();
}
private slots:
void childSucceded()
{
+ unsigned sleepTime = backoff();
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
+ {
emitSucceeded();
- // TODO: implement exponential backoff retry scheme with limit
- emitFailed("Failed to commit instance");
+ return;
+ }
+ // we actually failed, retry?
+ if(sleepTime == maxBackoff)
+ {
+ emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
+ return;
+ }
+ qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
+ m_backoffTimer.start(sleepTime * 500);
}
void childFailed(const QString & reason)
{
@@ -349,13 +397,38 @@ private slots:
}
private:
+ ExponentialSeries backoff;
QString m_stagingPath;
FolderInstanceProvider * m_parent;
unique_qobject_ptr<Task> m_child;
QString m_instanceName;
QString m_groupName;
+ QTimer m_backoffTimer;
};
-*/
+
+#include "InstanceImportTask.h"
+Task * FolderInstanceProvider::zipImportTask(const QUrl sourceUrl, const QString& instName, const QString& instGroup, const QString& instIcon)
+{
+ auto stagingPath = getStagedInstancePath();
+ auto task = new InstanceImportTask(m_globalSettings, sourceUrl, stagingPath, instName, instIcon, instGroup);
+ return new FolderInstanceStaging(this, task, stagingPath, instName, instGroup);
+}
+
+#include "InstanceCreationTask.h"
+Task * FolderInstanceProvider::creationTask(BaseVersionPtr version, const QString& instName, const QString& instGroup, const QString& instIcon)
+{
+ auto stagingPath = getStagedInstancePath();
+ auto task = new InstanceCreationTask(m_globalSettings, stagingPath, version, instName, instIcon, instGroup);
+ return new FolderInstanceStaging(this, task, stagingPath, instName, instGroup);
+}
+
+#include "InstanceCopyTask.h"
+Task * FolderInstanceProvider::copyTask(const InstancePtr& oldInstance, const QString& instName, const QString& instGroup, const QString& instIcon, bool copySaves)
+{
+ auto stagingPath = getStagedInstancePath();
+ auto task = new InstanceCopyTask(m_globalSettings, stagingPath, oldInstance, instName, instIcon, instGroup, copySaves);
+ return new FolderInstanceStaging(this, task, stagingPath, instName, instGroup);
+}
QString FolderInstanceProvider::getStagedInstancePath()
{
@@ -395,3 +468,4 @@ bool FolderInstanceProvider::destroyStagingPath(const QString& keyPath)
return FS::deletePath(keyPath);
}
+#include "FolderInstanceProvider.moc"