tst_QThreadPool: reduce the number of infinite timeouts

... by supplying a TestThreadPool that waitsForDone() with a defined
timeout in the dtor.

When tests hang, this will now print an intelligible message instead
of just the generic watchdog-killed-process one.

Also replace all QVERIFY(waitForDone()) with the same code used in
TestThreadPool's dtor and add a comment in a place we'd rather not use
these tools.

Pick-to: 6.5 6.2
Change-Id: Ifd2b3372eb7c7337a3ba77d003e45dcd77e23545
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Marc Mutz 2023-03-29 09:24:24 +02:00
parent eb260c132a
commit 1cc1d39f3e

View File

@ -138,10 +138,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);
}
@ -152,7 +167,7 @@ void tst_QThreadPool::runFunction2()
{
int localCount = 0;
{
QThreadPool manager;
TestThreadPool manager;
manager.start([&]() { ++localCount; });
}
QCOMPARE(localCount, 1);
@ -161,7 +176,7 @@ void tst_QThreadPool::runFunction2()
void tst_QThreadPool::createThreadRunFunction()
{
{
QThreadPool manager;
TestThreadPool manager;
testFunctionCount = 0;
manager.start(noSleepTestFunction);
}
@ -174,7 +189,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);
@ -183,7 +198,7 @@ void tst_QThreadPool::runMultiple()
QCOMPARE(testFunctionCount, runs);
{
QThreadPool manager;
TestThreadPool manager;
testFunctionCount = 0;
for (int i = 0; i < runs; ++i) {
manager.start(noSleepTestFunctionMutex);
@ -192,7 +207,7 @@ void tst_QThreadPool::runMultiple()
QCOMPARE(testFunctionCount, runs);
{
QThreadPool manager;
TestThreadPool manager;
for (int i = 0; i < 500; ++i)
manager.start(emptyFunct);
}
@ -203,6 +218,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);
}
@ -221,7 +237,7 @@ public:
void tst_QThreadPool::runTask()
{
QThreadPool manager;
TestThreadPool manager;
ran.storeRelaxed(false);
manager.start(new TestTask());
QTRY_VERIFY(ran.loadRelaxed());
@ -283,7 +299,7 @@ public:
*/
void tst_QThreadPool::threadRecycling()
{
QThreadPool threadPool;
TestThreadPool threadPool;
threadPool.start(new ThreadRecorderTask());
threadRecyclingSemaphore.acquire();
@ -311,7 +327,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());
@ -348,7 +364,7 @@ void tst_QThreadPool::expiryTimeout()
{
ExpiryTimeoutTask task;
QThreadPool threadPool;
TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
int expiryTimeout = threadPool.expiryTimeout();
@ -387,7 +403,7 @@ void tst_QThreadPool::expiryTimeoutRace() // QTBUG-3786
#endif
ExpiryTimeoutTask task;
QThreadPool threadPool;
TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
threadPool.setExpiryTimeout(50);
const int numTasks = 20;
@ -414,7 +430,7 @@ void tst_QThreadPool::exceptions()
{
ExceptionTask task;
{
QThreadPool threadPool;
TestThreadPool threadPool;
// Uncomment this for a nice crash.
// threadPool.start(&task);
}
@ -458,7 +474,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
@ -488,7 +504,7 @@ void tst_QThreadPool::setMaxThreadCountStartsAndStopsThreads()
}
};
QThreadPool threadPool;
TestThreadPool threadPool;
threadPool.setMaxThreadCount(-1); // docs say we'll always start at least one
WaitingTask task;
@ -576,7 +592,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
@ -640,7 +656,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
@ -841,7 +857,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());
}
@ -868,13 +884,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());
}
@ -905,7 +921,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)
@ -934,7 +950,7 @@ void tst_QThreadPool::tryStartCount()
};
SleeperTask task;
QThreadPool threadPool;
TestThreadPool threadPool;
const int runs = 5;
for (int i = 0; i < runs; ++i) {
@ -982,7 +998,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
@ -994,7 +1010,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);
}
@ -1004,7 +1020,7 @@ void tst_QThreadPool::waitForDone()
total.start();
pass.start();
QThreadPool threadPool;
TestThreadPool threadPool;
while (total.elapsed() < 10000) {
int runs;
count.storeRelaxed(runs = 0);
@ -1013,7 +1029,7 @@ void tst_QThreadPool::waitForDone()
threadPool.start(new CountingRunnable());
++runs;
}
threadPool.waitForDone();
WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), runs);
count.storeRelaxed(runs = 0);
@ -1023,7 +1039,7 @@ void tst_QThreadPool::waitForDone()
threadPool.start(new CountingRunnable());
runs += 2;
}
threadPool.waitForDone();
WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), runs);
}
}
@ -1045,7 +1061,7 @@ void tst_QThreadPool::waitForDoneTimeout()
}
};
QThreadPool threadPool;
TestThreadPool threadPool;
mutex.lock();
threadPool.start(new BlockedTask(mutex));
@ -1069,7 +1085,7 @@ void tst_QThreadPool::clear()
}
};
QThreadPool threadPool;
TestThreadPool threadPool;
threadPool.setMaxThreadCount(10);
int runs = 2 * threadPool.maxThreadCount();
count.storeRelaxed(0);
@ -1078,7 +1094,7 @@ void tst_QThreadPool::clear()
}
threadPool.clear();
sem.release(threadPool.maxThreadCount());
threadPool.waitForDone();
WAIT_FOR_DONE(threadPool);
QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount());
}
@ -1091,7 +1107,7 @@ void tst_QThreadPool::clearWithAutoDelete()
void run() override { QThread::sleep(30us); }
};
QThreadPool threadPool;
TestThreadPool threadPool;
threadPool.setMaxThreadCount(4);
const int loopCount = 20;
const int batchSize = 500;
@ -1104,7 +1120,6 @@ void tst_QThreadPool::clearWithAutoDelete()
threadPool.start(runnable);
}
}
QVERIFY(threadPool.waitForDone());
}
void tst_QThreadPool::tryTake()
@ -1144,7 +1159,7 @@ void tst_QThreadPool::tryTake()
Runs = MaxThreadCount * OverProvisioning
};
QThreadPool threadPool;
TestThreadPool threadPool;
threadPool.setMaxThreadCount(MaxThreadCount);
BlockingRunnable *runnables[Runs];
@ -1176,7 +1191,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));
@ -1193,7 +1208,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());
@ -1204,7 +1219,7 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish()
count.storeRelaxed(runs = 0);
{
QThreadPool threadPool;
TestThreadPool threadPool;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
@ -1246,10 +1261,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);
}
@ -1310,7 +1325,7 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
QSemaphore mainBarrier;
QSemaphore taskBarrier;
QThreadPool threadPool;
TestThreadPool threadPool;
threadPool.setMaxThreadCount(1);
Task task1(&mainBarrier, &taskBarrier);
@ -1341,7 +1356,7 @@ void tst_QThreadPool::takeAllAndIncreaseMaxThreadCount() {
taskBarrier.release(1);
threadPool.waitForDone();
WAIT_FOR_DONE(threadPool);
QCOMPARE(threadPool.activeThreadCount(), 0);
}
@ -1374,7 +1389,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
@ -1405,12 +1420,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.");
}
/*
@ -1420,7 +1429,7 @@ void tst_QThreadPool::waitForDoneAfterTake()
*/
void tst_QThreadPool::threadReuse()
{
QThreadPool manager;
TestThreadPool manager;
manager.setExpiryTimeout(-1);
manager.setMaxThreadCount(1);