diff options
Diffstat (limited to 'tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp')
-rw-r--r-- | tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp | 159 |
1 files changed, 106 insertions, 53 deletions
diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp index d263c5013a..2006016d47 100644 --- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp +++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp @@ -1,11 +1,12 @@ // Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 Intel Corporation. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSemaphore> #include <qelapsedtimer.h> +#include <qrunnable.h> #include <qthreadpool.h> #include <qstring.h> #include <qmutex.h> @@ -14,6 +15,8 @@ #include <unistd.h> #endif +using namespace std::chrono_literals; + typedef void (*FunctionPointer)(); class FunctionPointerTask : public QRunnable @@ -43,6 +46,7 @@ public: private slots: void runFunction(); void runFunction2(); + void runFunction3(); void createThreadRunFunction(); void runMultiple(); void waitcomplete(); @@ -83,6 +87,7 @@ private slots: void takeAllAndIncreaseMaxThreadCount(); void waitForDoneAfterTake(); void threadReuse(); + void nullFunctions(); private: QMutex m_functionTestMutex; @@ -136,10 +141,25 @@ void noSleepTestFunctionMutex() tst_QThreadPool::functionTestMutex->unlock(); } +constexpr int DefaultWaitForDoneTimeout = 1 * 60 * 1000; // 1min +// Using qFatal instead of QVERIFY to force exit if threads are still running after timeout. +// Otherwise, QCoreApplication will still wait for the stale threads and never exit the test. +#define WAIT_FOR_DONE(manager) \ + if ((manager).waitForDone(DefaultWaitForDoneTimeout)) {} else \ + qFatal("waitForDone returned false. Aborting to stop background threads.") + +// uses explicit timeout in dtor's waitForDone() to avoid tests hanging overly long +class TestThreadPool : public QThreadPool +{ +public: + using QThreadPool::QThreadPool; + ~TestThreadPool() { WAIT_FOR_DONE(*this); } +}; + void tst_QThreadPool::runFunction() { { - QThreadPool manager; + TestThreadPool manager; testFunctionCount = 0; manager.start(noSleepTestFunction); } @@ -150,16 +170,33 @@ void tst_QThreadPool::runFunction2() { int localCount = 0; { - QThreadPool manager; + TestThreadPool manager; manager.start([&]() { ++localCount; }); } QCOMPARE(localCount, 1); } +struct DeleteCheck +{ + static bool s_deleted; + ~DeleteCheck() { s_deleted = true; } +}; +bool DeleteCheck::s_deleted = false; + +void tst_QThreadPool::runFunction3() +{ + std::unique_ptr<DeleteCheck> ptr(new DeleteCheck); + { + TestThreadPool manager; + manager.start([my_ptr = std::move(ptr)]() { }); + } + QVERIFY(DeleteCheck::s_deleted); +} + void tst_QThreadPool::createThreadRunFunction() { { - QThreadPool manager; + TestThreadPool manager; testFunctionCount = 0; manager.start(noSleepTestFunction); } @@ -172,7 +209,7 @@ void tst_QThreadPool::runMultiple() const int runs = 10; { - QThreadPool manager; + TestThreadPool manager; testFunctionCount = 0; for (int i = 0; i < runs; ++i) { manager.start(sleepTestFunctionMutex); @@ -181,7 +218,7 @@ void tst_QThreadPool::runMultiple() QCOMPARE(testFunctionCount, runs); { - QThreadPool manager; + TestThreadPool manager; testFunctionCount = 0; for (int i = 0; i < runs; ++i) { manager.start(noSleepTestFunctionMutex); @@ -190,7 +227,7 @@ void tst_QThreadPool::runMultiple() QCOMPARE(testFunctionCount, runs); { - QThreadPool manager; + TestThreadPool manager; for (int i = 0; i < 500; ++i) manager.start(emptyFunct); } @@ -201,6 +238,7 @@ void tst_QThreadPool::waitcomplete() testFunctionCount = 0; const int runs = 500; for (int i = 0; i < 500; ++i) { + // TestThreadPool pool; // no, we're checking ~QThreadPool()'s waitForDone() QThreadPool pool; pool.start(noSleepTestFunction); } @@ -219,7 +257,7 @@ public: void tst_QThreadPool::runTask() { - QThreadPool manager; + TestThreadPool manager; ran.storeRelaxed(false); manager.start(new TestTask()); QTRY_VERIFY(ran.loadRelaxed()); @@ -281,7 +319,7 @@ public: */ void tst_QThreadPool::threadRecycling() { - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.start(new ThreadRecorderTask()); threadRecyclingSemaphore.acquire(); @@ -309,7 +347,7 @@ void tst_QThreadPool::threadRecycling() void tst_QThreadPool::threadPriority() { QThread::Priority priority = QThread::HighPriority; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setThreadPriority(priority); threadPool.start(new ThreadRecorderTask()); @@ -346,7 +384,7 @@ void tst_QThreadPool::expiryTimeout() { ExpiryTimeoutTask task; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(1); int expiryTimeout = threadPool.expiryTimeout(); @@ -380,18 +418,15 @@ void tst_QThreadPool::expiryTimeout() void tst_QThreadPool::expiryTimeoutRace() // QTBUG-3786 { -#ifdef Q_OS_WIN - QSKIP("This test is unstable on Windows. See QTBUG-3786."); -#endif ExpiryTimeoutTask task; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(1); threadPool.setExpiryTimeout(50); const int numTasks = 20; for (int i = 0; i < numTasks; ++i) { threadPool.start(&task); - QThread::msleep(50); // exactly the same as the expiry timeout + QThread::sleep(50ms); // exactly the same as the expiry timeout } QVERIFY(task.semaphore.tryAcquire(numTasks, 10000)); QCOMPARE(task.runCount.loadRelaxed(), numTasks); @@ -412,7 +447,7 @@ void tst_QThreadPool::exceptions() { ExceptionTask task; { - QThreadPool threadPool; + TestThreadPool threadPool; // Uncomment this for a nice crash. // threadPool.start(&task); } @@ -456,7 +491,7 @@ void tst_QThreadPool::setMaxThreadCount() // setting the limit on children should have no effect on the parent { - QThreadPool threadPool2(threadPool); + TestThreadPool threadPool2(threadPool); savedLimit = threadPool2.maxThreadCount(); // maxThreadCount() should always return the previous argument to @@ -486,7 +521,7 @@ void tst_QThreadPool::setMaxThreadCountStartsAndStopsThreads() } }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(-1); // docs say we'll always start at least one WaitingTask task; @@ -574,7 +609,7 @@ void tst_QThreadPool::reserveThread() // reserving threads in children should not effect the parent { - QThreadPool threadpool2(threadpool); + TestThreadPool threadpool2(threadpool); threadpool2.setMaxThreadCount(limit); // reserve up to the limit @@ -638,7 +673,7 @@ void tst_QThreadPool::releaseThread() // releasing threads in children should not effect the parent { - QThreadPool threadpool2(threadpool); + TestThreadPool threadpool2(threadpool); threadpool2.setMaxThreadCount(limit); // reserve up to the limit @@ -839,7 +874,7 @@ void tst_QThreadPool::start() const int runs = 1000; count.storeRelaxed(0); { - QThreadPool threadPool; + TestThreadPool threadPool; for (int i = 0; i< runs; ++i) { threadPool.start(new CountingRunnable()); } @@ -866,13 +901,13 @@ void tst_QThreadPool::tryStart() count.storeRelaxed(0); WaitingTask task; - QThreadPool threadPool; + TestThreadPool threadPool; for (int i = 0; i < threadPool.maxThreadCount(); ++i) { threadPool.start(&task); } QVERIFY(!threadPool.tryStart(&task)); task.semaphore.release(threadPool.maxThreadCount()); - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount()); } @@ -903,7 +938,7 @@ void tst_QThreadPool::tryStartPeakThreadCount() }; CounterTask task; - QThreadPool threadPool; + TestThreadPool threadPool; for (int i = 0; i < 4*QThread::idealThreadCount(); ++i) { if (threadPool.tryStart(&task) == false) @@ -932,7 +967,7 @@ void tst_QThreadPool::tryStartCount() }; SleeperTask task; - QThreadPool threadPool; + TestThreadPool threadPool; const int runs = 5; for (int i = 0; i < runs; ++i) { @@ -980,7 +1015,7 @@ void tst_QThreadPool::priorityStart() QSemaphore sem; QAtomicPointer<QRunnable> firstStarted; QRunnable *expected; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(1); // start only one thread at a time // queue the holder first @@ -992,7 +1027,7 @@ void tst_QThreadPool::priorityStart() threadPool.start(expected = new Runner(firstStarted), 1); // priority 1 sem.release(); - QVERIFY(threadPool.waitForDone()); + WAIT_FOR_DONE(threadPool); QCOMPARE(firstStarted.loadRelaxed(), expected); } @@ -1002,7 +1037,7 @@ void tst_QThreadPool::waitForDone() total.start(); pass.start(); - QThreadPool threadPool; + TestThreadPool threadPool; while (total.elapsed() < 10000) { int runs; count.storeRelaxed(runs = 0); @@ -1011,7 +1046,7 @@ void tst_QThreadPool::waitForDone() threadPool.start(new CountingRunnable()); ++runs; } - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(count.loadRelaxed(), runs); count.storeRelaxed(runs = 0); @@ -1021,7 +1056,7 @@ void tst_QThreadPool::waitForDone() threadPool.start(new CountingRunnable()); runs += 2; } - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(count.loadRelaxed(), runs); } } @@ -1043,7 +1078,7 @@ void tst_QThreadPool::waitForDoneTimeout() } }; - QThreadPool threadPool; + TestThreadPool threadPool; mutex.lock(); threadPool.start(new BlockedTask(mutex)); @@ -1067,7 +1102,7 @@ void tst_QThreadPool::clear() } }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(10); int runs = 2 * threadPool.maxThreadCount(); count.storeRelaxed(0); @@ -1076,7 +1111,7 @@ void tst_QThreadPool::clear() } threadPool.clear(); sem.release(threadPool.maxThreadCount()); - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount()); } @@ -1086,10 +1121,10 @@ void tst_QThreadPool::clearWithAutoDelete() { public: MyRunnable() {} - void run() override { QThread::usleep(30); } + void run() override { QThread::sleep(30us); } }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(4); const int loopCount = 20; const int batchSize = 500; @@ -1102,7 +1137,6 @@ void tst_QThreadPool::clearWithAutoDelete() threadPool.start(runnable); } } - QVERIFY(threadPool.waitForDone()); } void tst_QThreadPool::tryTake() @@ -1142,7 +1176,7 @@ void tst_QThreadPool::tryTake() Runs = MaxThreadCount * OverProvisioning }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(MaxThreadCount); BlockingRunnable *runnables[Runs]; @@ -1174,7 +1208,7 @@ void tst_QThreadPool::tryTake() runnables[0]->dummy = 0; // valgrind will catch this if tryTake() is crazy enough to delete currently running jobs QCOMPARE(dtorCounter.loadRelaxed(), int(Runs - MaxThreadCount)); sem.release(MaxThreadCount); - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(runCounter.loadRelaxed(), int(MaxThreadCount)); QCOMPARE(count.loadRelaxed(), int(MaxThreadCount)); QCOMPARE(dtorCounter.loadRelaxed(), int(Runs - 1)); @@ -1191,7 +1225,7 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish() int runs; count.storeRelaxed(runs = 0); { - QThreadPool threadPool; + TestThreadPool threadPool; pass.restart(); while (pass.elapsed() < 100) { threadPool.start(new CountingRunnable()); @@ -1202,7 +1236,7 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish() count.storeRelaxed(runs = 0); { - QThreadPool threadPool; + TestThreadPool threadPool; pass.restart(); while (pass.elapsed() < 100) { threadPool.start(new CountingRunnable()); @@ -1244,10 +1278,10 @@ void tst_QThreadPool::stackSize() } }; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setStackSize(targetStackSize); threadPool.start(new StackSizeChecker(&threadStackSize)); - QVERIFY(threadPool.waitForDone(30000)); // 30s timeout + WAIT_FOR_DONE(threadPool); QCOMPARE(threadStackSize, targetStackSize); } @@ -1308,7 +1342,7 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() { QSemaphore mainBarrier; QSemaphore taskBarrier; - QThreadPool threadPool; + TestThreadPool threadPool; threadPool.setMaxThreadCount(1); Task task1(&mainBarrier, &taskBarrier); @@ -1339,7 +1373,7 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() { taskBarrier.release(1); - threadPool.waitForDone(); + WAIT_FOR_DONE(threadPool); QCOMPARE(threadPool.activeThreadCount(), 0); } @@ -1372,7 +1406,7 @@ void tst_QThreadPool::waitForDoneAfterTake() // Blocks the tasks from completing their run function QSemaphore threadBarrier; - QThreadPool manager; + TestThreadPool manager; manager.setMaxThreadCount(threadCount); // Fill all the threads with runnables that wait for the threadBarrier @@ -1403,12 +1437,6 @@ void tst_QThreadPool::waitForDoneAfterTake() // Release runnables that are waiting and expect all runnables to complete threadBarrier.release(threadCount); - - // Using qFatal instead of QVERIFY to force exit if threads are still running after timeout. - // Otherwise, QCoreApplication will still wait for the stale threads and never exit the test. - if (!manager.waitForDone(5 * 60 * 1000)) - qFatal("waitForDone returned false. Aborting to stop background threads."); - } /* @@ -1418,7 +1446,7 @@ void tst_QThreadPool::waitForDoneAfterTake() */ void tst_QThreadPool::threadReuse() { - QThreadPool manager; + TestThreadPool manager; manager.setExpiryTimeout(-1); manager.setMaxThreadCount(1); @@ -1435,5 +1463,30 @@ void tst_QThreadPool::threadReuse() } } +void tst_QThreadPool::nullFunctions() +{ + const auto expectWarning = [] { + QTest::ignoreMessage(QtMsgType::QtWarningMsg, + "Trying to create null QRunnable. This may stop working."); + }; + // Note this is not necessarily testing intended behavior, only undocumented behavior. + // If this is changed it should be noted in Behavioral Changes. + FunctionPointer nullFunction = nullptr; + std::function<void()> nullStdFunction(nullptr); + { + TestThreadPool manager; + // should not crash: + expectWarning(); + manager.start(nullFunction); + expectWarning(); + manager.start(nullStdFunction); + // should fail (and not leak): + expectWarning(); + QVERIFY(!manager.tryStart(nullStdFunction)); + expectWarning(); + QVERIFY(!manager.tryStart(nullFunction)); + } +} + QTEST_MAIN(tst_QThreadPool); #include "tst_qthreadpool.moc" |