diff --git a/launcher/tasks/ConcurrentTask.cpp b/launcher/tasks/ConcurrentTask.cpp index a890013ef..190d48d8f 100644 --- a/launcher/tasks/ConcurrentTask.cpp +++ b/launcher/tasks/ConcurrentTask.cpp @@ -110,14 +110,14 @@ void ConcurrentTask::startNext() setStepStatus(next->isMultiStep() ? next->getStepStatus() : next->getStatus()); updateState(); + QCoreApplication::processEvents(); + QMetaObject::invokeMethod(next.get(), &Task::start, Qt::QueuedConnection); // Allow going up the number of concurrent tasks in case of tasks being added in the middle of a running task. int num_starts = m_total_max_size - m_doing.size(); for (int i = 0; i < num_starts; i++) QMetaObject::invokeMethod(this, &ConcurrentTask::startNext, Qt::QueuedConnection); - - QCoreApplication::processEvents(); } void ConcurrentTask::subTaskSucceeded(Task::Ptr task) diff --git a/tests/Task_test.cpp b/tests/Task_test.cpp index 80bba02fc..6649b7248 100644 --- a/tests/Task_test.cpp +++ b/tests/Task_test.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include @@ -11,6 +13,9 @@ class BasicTask : public Task { friend class TaskTest; + public: + BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {} + private: void executeTask() override { @@ -30,6 +35,42 @@ class BasicTask_MultiStep : public Task { void executeTask() override {}; }; +class BigConcurrentTask : public QThread { + Q_OBJECT + + ConcurrentTask big_task; + + void run() override + { + QTimer deadline; + deadline.setInterval(10000); + connect(&deadline, &QTimer::timeout, this, [this]{ passed_the_deadline = true; }); + deadline.start(); + + // NOTE: Arbitrary value that manages to trigger a problem when there is one. + static const unsigned s_num_tasks = 1 << 14; + auto sub_tasks = new BasicTask*[s_num_tasks]; + + for (unsigned i = 0; i < s_num_tasks; i++) { + sub_tasks[i] = new BasicTask(false); + big_task.addTask(sub_tasks[i]); + } + + big_task.run(); + + while (!big_task.isFinished() && !passed_the_deadline) + QCoreApplication::processEvents(); + + emit finished(); + } + + public: + bool passed_the_deadline = false; + + signals: + void finished(); +}; + class TaskTest : public QObject { Q_OBJECT @@ -183,6 +224,25 @@ class TaskTest : public QObject { return t.isFinished(); }, 1000), "Task didn't finish as it should."); } + + void test_stackOverflowInConcurrentTask() + { + QEventLoop loop; + + auto thread = new BigConcurrentTask; + // NOTE: This is an arbitrary value, big enough to not cause problems on normal execution, but low enough + // so that the number of tasks that needs to get ran to potentially cause a problem isn't too big. + thread->setStackSize(32 * 1024); + + connect(thread, &BigConcurrentTask::finished, &loop, &QEventLoop::quit); + + thread->start(); + + loop.exec(); + + QVERIFY(!thread->passed_the_deadline); + thread->deleteLater(); + } }; QTEST_GUILESS_MAIN(TaskTest)