fix: Task test memory leaks again

Signed-off-by: Rachel Powers <508861+Ryex@users.noreply.github.com>
This commit is contained in:
Rachel Powers 2023-07-05 11:51:37 -07:00
parent 5fe9a7a6c3
commit 4dbcedd03f
No known key found for this signature in database
GPG Key ID: E10E321EB160949B
2 changed files with 72 additions and 71 deletions

View File

@ -95,18 +95,23 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND DEBUG_ADDRESS_SANITIZER)
# using clang with clang-cl front end # using clang with clang-cl front end
message(STATUS "Address Sanitizer available on Clang MSVC frontend") message(STATUS "Address Sanitizer available on Clang MSVC frontend")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
else() else()
# AppleClang and Clang # AppleClang and Clang
message(STATUS "Address Sanitizer available on Clang") message(STATUS "Address Sanitizer available on Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
endif() endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# GCC # GCC
message(STATUS "Address Sanitizer available on GCC") message(STATUS "Address Sanitizer available on GCC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -O1 -fno-omit-frame-pointer")
link_libraries("asan")
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
message(STATUS "Address Sanitizer available on MSVC") message(STATUS "Address Sanitizer available on MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /O1 /Oy-")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /O1 /Oy-")
else() else()
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}") message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
endif() endif()

View File

@ -1,6 +1,6 @@
#include <QTest> #include <QTest>
#include <QTimer>
#include <QThread> #include <QThread>
#include <QTimer>
#include <tasks/ConcurrentTask.h> #include <tasks/ConcurrentTask.h>
#include <tasks/MultipleOptionsTask.h> #include <tasks/MultipleOptionsTask.h>
@ -19,10 +19,7 @@ class BasicTask : public Task {
BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {} BasicTask(bool show_debug_log = true) : Task(nullptr, show_debug_log) {}
private: private:
void executeTask() override void executeTask() override { emitSucceeded(); };
{
emitSucceeded();
};
}; };
/* Does nothing. Only used for testing. */ /* Does nothing. Only used for testing. */
@ -34,7 +31,7 @@ class BasicTask_MultiStep : public Task {
private: private:
auto isMultiStep() const -> bool override { return true; } auto isMultiStep() const -> bool override { return true; }
void executeTask() override {}; void executeTask() override{};
}; };
class BigConcurrentTask : public ConcurrentTask { class BigConcurrentTask : public ConcurrentTask {
@ -44,7 +41,7 @@ class BigConcurrentTask : public ConcurrentTask {
{ {
// This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^)) // This is here only to help fill the stack a bit more quickly (if there's an issue, of course :^))
// Each tasks thus adds 1024 * 4 bytes to the stack, at the very least. // Each tasks thus adds 1024 * 4 bytes to the stack, at the very least.
[[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack {}; [[maybe_unused]] volatile std::array<uint32_t, 1024> some_data_on_the_stack{};
ConcurrentTask::startNext(); ConcurrentTask::startNext();
} }
@ -59,28 +56,28 @@ class BigConcurrentTaskThread : public QThread {
{ {
QTimer deadline; QTimer deadline;
deadline.setInterval(10000); deadline.setInterval(10000);
connect(&deadline, &QTimer::timeout, this, [this]{ passed_the_deadline = true; }); connect(&deadline, &QTimer::timeout, this, [this] { passed_the_deadline = true; });
deadline.start(); deadline.start();
// NOTE: Arbitrary value that manages to trigger a problem when there is one. // NOTE: Arbitrary value that manages to trigger a problem when there is one.
// Considering each tasks, in a problematic state, adds 1024 * 4 bytes to the stack, // Considering each tasks, in a problematic state, adds 1024 * 4 bytes to the stack,
// this number is enough to fill up 16 MiB of stack, more than enough to cause a problem. // this number is enough to fill up 16 MiB of stack, more than enough to cause a problem.
static const unsigned s_num_tasks = 1 << 12; static const unsigned s_num_tasks = 1 << 12;
auto sub_tasks = new BasicTask::Ptr[s_num_tasks]; {
auto sub_tasks = std::array<BasicTask::Ptr, s_num_tasks>();
for (unsigned i = 0; i < s_num_tasks; i++) { for (unsigned i = 0; i < s_num_tasks; i++) {
auto sub_task = makeShared<BasicTask>(false); auto sub_task = makeShared<BasicTask>(false);
sub_tasks[i] = sub_task; sub_tasks[i] = sub_task;
big_task.addTask(sub_task); big_task.addTask(sub_task);
} }
big_task.run(); big_task.run();
while (!big_task.isFinished() && !passed_the_deadline)
QCoreApplication::processEvents();
while (!big_task.isFinished() && !passed_the_deadline)
QCoreApplication::processEvents();
} // drop before emit
emit finished(); emit finished();
delete[] sub_tasks;
} }
public: public:
@ -94,9 +91,10 @@ class TaskTest : public QObject {
Q_OBJECT Q_OBJECT
private slots: private slots:
void test_SetStatus_NoMultiStep(){ void test_SetStatus_NoMultiStep()
{
BasicTask t; BasicTask t;
QString status {"test status"}; QString status{ "test status" };
t.setStatus(status); t.setStatus(status);
@ -104,9 +102,10 @@ class TaskTest : public QObject {
QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
} }
void test_SetStatus_MultiStep(){ void test_SetStatus_MultiStep()
{
BasicTask_MultiStep t; BasicTask_MultiStep t;
QString status {"test status"}; QString status{ "test status" };
t.setStatus(status); t.setStatus(status);
@ -116,7 +115,8 @@ class TaskTest : public QObject {
QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty()); QCOMPARE(t.getStepProgress().isEmpty(), TaskStepProgressList{}.isEmpty());
} }
void test_SetProgress(){ void test_SetProgress()
{
BasicTask t; BasicTask t;
int current = 42; int current = 42;
int total = 207; int total = 207;
@ -127,17 +127,18 @@ class TaskTest : public QObject {
QCOMPARE(t.getTotalProgress(), total); QCOMPARE(t.getTotalProgress(), total);
} }
void test_basicRun(){ void test_basicRun()
{
BasicTask t; BasicTask t;
QObject::connect(&t, &Task::finished, [&]{ QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); }); QObject::connect(&t, &Task::finished,
[&] { QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
void test_basicConcurrentRun(){ void test_basicConcurrentRun()
{
auto t1 = makeShared<BasicTask>(); auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>(); auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>(); auto t3 = makeShared<BasicTask>();
@ -148,21 +149,20 @@ class TaskTest : public QObject {
t.addTask(t2); t.addTask(t2);
t.addTask(t3); t.addTask(t3);
QObject::connect(&t, &Task::finished, [&]{ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
QVERIFY(t1->wasSuccessful()); QVERIFY(t1->wasSuccessful());
QVERIFY(t2->wasSuccessful()); QVERIFY(t2->wasSuccessful());
QVERIFY(t3->wasSuccessful()); QVERIFY(t3->wasSuccessful());
}); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
// Tests if starting new tasks after the 6 initial ones is working // Tests if starting new tasks after the 6 initial ones is working
void test_moreConcurrentRun(){ void test_moreConcurrentRun()
{
auto t1 = makeShared<BasicTask>(); auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>(); auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>(); auto t3 = makeShared<BasicTask>();
@ -185,26 +185,25 @@ class TaskTest : public QObject {
t.addTask(t8); t.addTask(t8);
t.addTask(t9); t.addTask(t9);
QObject::connect(&t, &Task::finished, [&]{ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9] {
QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
QVERIFY(t1->wasSuccessful()); QVERIFY(t1->wasSuccessful());
QVERIFY(t2->wasSuccessful()); QVERIFY(t2->wasSuccessful());
QVERIFY(t3->wasSuccessful()); QVERIFY(t3->wasSuccessful());
QVERIFY(t4->wasSuccessful()); QVERIFY(t4->wasSuccessful());
QVERIFY(t5->wasSuccessful()); QVERIFY(t5->wasSuccessful());
QVERIFY(t6->wasSuccessful()); QVERIFY(t6->wasSuccessful());
QVERIFY(t7->wasSuccessful()); QVERIFY(t7->wasSuccessful());
QVERIFY(t8->wasSuccessful()); QVERIFY(t8->wasSuccessful());
QVERIFY(t9->wasSuccessful()); QVERIFY(t9->wasSuccessful());
}); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
void test_basicSequentialRun(){ void test_basicSequentialRun()
{
auto t1 = makeShared<BasicTask>(); auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>(); auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>(); auto t3 = makeShared<BasicTask>();
@ -215,20 +214,19 @@ class TaskTest : public QObject {
t.addTask(t2); t.addTask(t2);
t.addTask(t3); t.addTask(t3);
QObject::connect(&t, &Task::finished, [&]{ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
QVERIFY(t1->wasSuccessful()); QVERIFY(t1->wasSuccessful());
QVERIFY(t2->wasSuccessful()); QVERIFY(t2->wasSuccessful());
QVERIFY(t3->wasSuccessful()); QVERIFY(t3->wasSuccessful());
}); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
void test_basicMultipleOptionsRun(){ void test_basicMultipleOptionsRun()
{
auto t1 = makeShared<BasicTask>(); auto t1 = makeShared<BasicTask>();
auto t2 = makeShared<BasicTask>(); auto t2 = makeShared<BasicTask>();
auto t3 = makeShared<BasicTask>(); auto t3 = makeShared<BasicTask>();
@ -239,17 +237,15 @@ class TaskTest : public QObject {
t.addTask(t2); t.addTask(t2);
t.addTask(t3); t.addTask(t3);
QObject::connect(&t, &Task::finished, [&]{ QObject::connect(&t, &Task::finished, [&t, &t1, &t2, &t3] {
QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been."); QVERIFY2(t.wasSuccessful(), "Task finished but was not successful when it should have been.");
QVERIFY(t1->wasSuccessful()); QVERIFY(t1->wasSuccessful());
QVERIFY(!t2->wasSuccessful()); QVERIFY(!t2->wasSuccessful());
QVERIFY(!t3->wasSuccessful()); QVERIFY(!t3->wasSuccessful());
}); });
t.start(); t.start();
QVERIFY2(QTest::qWaitFor([&]() { QVERIFY2(QTest::qWaitFor([&]() { return t.isFinished(); }, 1000), "Task didn't finish as it should.");
return t.isFinished();
}, 1000), "Task didn't finish as it should.");
} }
void test_stackOverflowInConcurrentTask() void test_stackOverflowInConcurrentTask()