summaryrefslogtreecommitdiffstats
path: root/tests/auto/qthreadpool/tst_qthreadpool.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qthreadpool/tst_qthreadpool.cpp')
-rw-r--r--tests/auto/qthreadpool/tst_qthreadpool.cpp841
1 files changed, 841 insertions, 0 deletions
diff --git a/tests/auto/qthreadpool/tst_qthreadpool.cpp b/tests/auto/qthreadpool/tst_qthreadpool.cpp
new file mode 100644
index 0000000..9bb39d0
--- /dev/null
+++ b/tests/auto/qthreadpool/tst_qthreadpool.cpp
@@ -0,0 +1,841 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtTest/QtTest>
+#include <qdatetime.h>
+#include <qthreadpool.h>
+#include <qstring.h>
+#include <qmutex.h>
+
+typedef void (*FunctionPointer)();
+
+class FunctionPointerTask : public QRunnable
+{
+public:
+ FunctionPointerTask(FunctionPointer function)
+ :function(function) {}
+ void run() { function(); }
+private:
+ FunctionPointer function;
+};
+
+QRunnable *createTask(FunctionPointer pointer)
+{
+ return new FunctionPointerTask(pointer);
+}
+
+class tst_QThreadPool : public QObject
+{
+ Q_OBJECT
+private slots:
+ void runFunction();
+ void createThreadRunFunction();
+ void runMultiple();
+ void waitcomplete();
+ void runTask();
+ void singleton();
+ void destruction();
+ void threadRecycling();
+ void expiryTimeout();
+ void exceptions();
+ void maxThreadCount();
+ void setMaxThreadCount_data();
+ void setMaxThreadCount();
+ void setMaxThreadCountStartsAndStopsThreads();
+ void activeThreadCount();
+ void reserveThread_data();
+ void reserveThread();
+ void releaseThread_data();
+ void releaseThread();
+ void start();
+ void tryStart();
+ void tryStartPeakThreadCount();
+ void tryStartCount();
+ void waitForDone();
+ void destroyingWaitsForTasksToFinish();
+ void stressTest();
+};
+
+int testFunctionCount;
+
+void sleepTestFunction()
+{
+ QTest::qSleep(1000);
+ ++testFunctionCount;
+}
+
+void emptyFunct()
+{
+
+}
+
+void noSleepTestFunction()
+{
+ ++testFunctionCount;
+}
+
+void sleepTestFunctionMutex()
+{
+ static QMutex testMutex;
+ QTest::qSleep(1000);
+ testMutex.lock();
+ ++testFunctionCount;
+ testMutex.unlock();
+}
+
+void noSleepTestFunctionMutex()
+{
+ static QMutex testMutex;
+ testMutex.lock();
+ ++testFunctionCount;
+ testMutex.unlock();
+}
+
+void tst_QThreadPool::runFunction()
+{
+ {
+ QThreadPool manager;
+ testFunctionCount = 0;
+ manager.start(createTask(noSleepTestFunction));
+ }
+ QCOMPARE(testFunctionCount, 1);
+}
+
+void tst_QThreadPool::createThreadRunFunction()
+{
+ {
+ QThreadPool manager;
+ testFunctionCount = 0;
+ manager.start(createTask(noSleepTestFunction));
+ }
+
+ QCOMPARE(testFunctionCount, 1);
+}
+
+void tst_QThreadPool::runMultiple()
+{
+ const int runs = 10;
+
+ {
+ QThreadPool manager;
+ testFunctionCount = 0;
+ for (int i = 0; i < runs; ++i) {
+ manager.start(createTask(sleepTestFunctionMutex));
+ }
+ }
+ QCOMPARE(testFunctionCount, runs);
+
+ {
+ QThreadPool manager;
+ testFunctionCount = 0;
+ for (int i = 0; i < runs; ++i) {
+ manager.start(createTask(noSleepTestFunctionMutex));
+ }
+ }
+ QCOMPARE(testFunctionCount, runs);
+
+ {
+ QThreadPool manager;
+ for (int i = 0; i < 500; ++i)
+ manager.start(createTask(emptyFunct));
+ }
+}
+
+void tst_QThreadPool::waitcomplete()
+{
+ testFunctionCount = 0;
+ const int runs = 500;
+ for (int i = 0; i < 500; ++i) {
+ QThreadPool pool;
+ pool.start(createTask(noSleepTestFunction));
+ }
+ QCOMPARE(testFunctionCount, runs);
+}
+
+volatile bool ran;
+class TestTask : public QRunnable
+{
+public:
+ void run()
+ {
+ ran = true;
+ }
+};
+
+void tst_QThreadPool::runTask()
+{
+ QThreadPool manager;
+ ran = false;
+ manager.start(new TestTask());
+ // Hang if task is not runned.
+ while (ran == false);
+}
+
+/*
+ Test running via QThreadPool::globalInstance()
+*/
+void tst_QThreadPool::singleton()
+{
+ ran = false;
+ QThreadPool::globalInstance()->start(new TestTask());
+ while (ran == false);
+}
+
+int *value = 0;
+class IntAccessor : public QRunnable
+{
+public:
+ void run()
+ {
+ for (int i = 0; i < 100; ++i) {
+ ++(*value);
+ QTest::qSleep(1);
+ }
+ }
+};
+
+/*
+ Test that the ThreadManager destructor waits until
+ all threads have completed.
+*/
+void tst_QThreadPool::destruction()
+{
+ value = new int;
+ QThreadPool *threadManager = new QThreadPool();
+ threadManager->start(new IntAccessor());
+ threadManager->start(new IntAccessor());
+ delete threadManager;
+ delete value;
+ value = 0;
+}
+
+QSemaphore threadRecyclingSemaphore;
+QThread *recycledThread = 0;
+
+class ThreadRecorderTask : public QRunnable
+{
+public:
+ void run()
+ {
+ recycledThread = QThread::currentThread();
+ threadRecyclingSemaphore.release();
+ }
+};
+
+/*
+ Test that the thread pool really reuses threads.
+*/
+void tst_QThreadPool::threadRecycling()
+{
+ QThreadPool threadPool;
+
+ threadPool.start(new ThreadRecorderTask());
+ threadRecyclingSemaphore.acquire();
+ QThread *thread1 = recycledThread;
+
+ QTest::qSleep(100);
+
+ threadPool.start(new ThreadRecorderTask());
+ threadRecyclingSemaphore.acquire();
+ QThread *thread2 = recycledThread;
+ QCOMPARE(thread1, thread2);
+
+ QTest::qSleep(100);
+
+ threadPool.start(new ThreadRecorderTask());
+ threadRecyclingSemaphore.acquire();
+ QThread *thread3 = recycledThread;
+ QCOMPARE(thread2, thread3);
+}
+
+class ExpiryTimeoutTask : public QRunnable
+{
+public:
+ QThread *thread;
+ int runCount;
+ QSemaphore semaphore;
+
+ ExpiryTimeoutTask()
+ : thread(0), runCount(0)
+ {
+ setAutoDelete(false);
+ }
+
+ void run()
+ {
+ thread = QThread::currentThread();
+ ++runCount;
+ semaphore.release();
+ }
+};
+
+void tst_QThreadPool::expiryTimeout()
+{
+ ExpiryTimeoutTask task;
+
+ QThreadPool threadPool;
+ threadPool.setMaxThreadCount(1);
+
+ int expiryTimeout = threadPool.expiryTimeout();
+ threadPool.setExpiryTimeout(1000);
+ QCOMPARE(threadPool.expiryTimeout(), 1000);
+
+ // run the task
+ threadPool.start(&task);
+ QVERIFY(task.semaphore.tryAcquire(1, 10000));
+ QCOMPARE(task.runCount, 1);
+ QVERIFY(!task.thread->wait(100));
+ // thread should expire
+ QThread *firstThread = task.thread;
+ QVERIFY(task.thread->wait(10000));
+
+ // run task again, thread should be restarted
+ threadPool.start(&task);
+ QVERIFY(task.semaphore.tryAcquire(1, 10000));
+ QCOMPARE(task.runCount, 2);
+ QVERIFY(!task.thread->wait(100));
+ // thread should expire again
+ QVERIFY(task.thread->wait(10000));
+
+ // thread pool should have reused the expired thread (instead of
+ // starting a new one)
+ QCOMPARE(firstThread, task.thread);
+
+ threadPool.setExpiryTimeout(expiryTimeout);
+ QCOMPARE(threadPool.expiryTimeout(), expiryTimeout);
+}
+
+#ifndef QT_NO_EXCEPTIONS
+class ExceptionTask : public QRunnable
+{
+public:
+ void run()
+ {
+ throw new int;
+ }
+};
+#endif
+
+void tst_QThreadPool::exceptions()
+{
+#ifndef QT_NO_EXCEPTIONS
+ ExceptionTask task;
+ {
+ QThreadPool threadPool;
+// Uncomment this for a nice crash.
+// threadPool.start(&task);
+ }
+#else
+ QSKIP("No exception support", SkipAll);
+#endif
+}
+
+void tst_QThreadPool::maxThreadCount()
+{
+ DEPENDS_ON("setMaxThreadCount()");
+}
+
+void tst_QThreadPool::setMaxThreadCount_data()
+{
+ QTest::addColumn<int>("limit");
+
+ QTest::newRow("") << 1;
+ QTest::newRow("") << -1;
+ QTest::newRow("") << 2;
+ QTest::newRow("") << -2;
+ QTest::newRow("") << 4;
+ QTest::newRow("") << -4;
+ QTest::newRow("") << 0;
+ QTest::newRow("") << 12345;
+ QTest::newRow("") << -6789;
+ QTest::newRow("") << 42;
+ QTest::newRow("") << -666;
+}
+
+void tst_QThreadPool::setMaxThreadCount()
+{
+ QFETCH(int, limit);
+ QThreadPool *threadPool = QThreadPool::globalInstance();
+ int savedLimit = threadPool->maxThreadCount();
+
+ // maxThreadCount() should always return the previous argument to
+ // setMaxThreadCount(), regardless of input
+ threadPool->setMaxThreadCount(limit);
+ QCOMPARE(threadPool->maxThreadCount(), limit);
+
+ // the value returned from maxThreadCount() should always be valid input for setMaxThreadCount()
+ threadPool->setMaxThreadCount(savedLimit);
+ QCOMPARE(threadPool->maxThreadCount(), savedLimit);
+
+ // setting the limit on children should have no effect on the parent
+ {
+ QThreadPool threadPool2(threadPool);
+ savedLimit = threadPool2.maxThreadCount();
+
+ // maxThreadCount() should always return the previous argument to
+ // setMaxThreadCount(), regardless of input
+ threadPool2.setMaxThreadCount(limit);
+ QCOMPARE(threadPool2.maxThreadCount(), limit);
+
+ // the value returned from maxThreadCount() should always be valid input for setMaxThreadCount()
+ threadPool2.setMaxThreadCount(savedLimit);
+ QCOMPARE(threadPool2.maxThreadCount(), savedLimit);
+ }
+}
+
+void tst_QThreadPool::setMaxThreadCountStartsAndStopsThreads()
+{
+ class WaitingTask : public QRunnable
+ {
+ public:
+ QSemaphore waitForStarted, waitToFinish;
+
+ WaitingTask() { setAutoDelete(false); }
+
+ void run()
+ {
+ waitForStarted.release();
+ waitToFinish.acquire();
+ }
+ };
+
+ QThreadPool threadPool;
+ threadPool.setMaxThreadCount(1);
+
+ WaitingTask *task = new WaitingTask;
+ threadPool.start(task);
+ QVERIFY(task->waitForStarted.tryAcquire(1, 1000));
+
+ // thread limit is 1, cannot start more tasks
+ threadPool.start(task);
+ QVERIFY(!task->waitForStarted.tryAcquire(1, 1000));
+
+ // increasing the limit by 1 should start the task immediately
+ threadPool.setMaxThreadCount(2);
+ QVERIFY(task->waitForStarted.tryAcquire(1, 1000));
+
+ // ... but we still cannot start more tasks
+ threadPool.start(task);
+ QVERIFY(!task->waitForStarted.tryAcquire(1, 1000));
+
+ // increasing the limit should be able to start more than one at a time
+ threadPool.start(task);
+ threadPool.setMaxThreadCount(4);
+ QVERIFY(task->waitForStarted.tryAcquire(2, 1000));
+
+ // ... but we still cannot start more tasks
+ threadPool.start(task);
+ threadPool.start(task);
+ QVERIFY(!task->waitForStarted.tryAcquire(2, 1000));
+
+ // decreasing the thread limit should cause the active thread count to go down
+ threadPool.setMaxThreadCount(2);
+ QCOMPARE(threadPool.activeThreadCount(), 4);
+ task->waitToFinish.release(2);
+ QTest::qWait(1000);
+ QCOMPARE(threadPool.activeThreadCount(), 2);
+
+ // ... and we still cannot start more tasks
+ threadPool.start(task);
+ threadPool.start(task);
+ QVERIFY(!task->waitForStarted.tryAcquire(2, 1000));
+
+ // start all remaining tasks
+ threadPool.start(task);
+ threadPool.start(task);
+ threadPool.start(task);
+ threadPool.start(task);
+ threadPool.setMaxThreadCount(8);
+ QVERIFY(task->waitForStarted.tryAcquire(6, 1000));
+
+ task->waitToFinish.release(10);
+// delete task;
+}
+
+
+void tst_QThreadPool::activeThreadCount()
+{
+ DEPENDS_ON("tryReserveThread()");
+ DEPENDS_ON("reserveThread()");
+ DEPENDS_ON("releaseThread()");
+}
+
+void tst_QThreadPool::reserveThread_data()
+{
+ setMaxThreadCount_data();
+}
+
+void tst_QThreadPool::reserveThread()
+{
+ QFETCH(int, limit);
+ QThreadPool *threadpool = QThreadPool::globalInstance();
+ int savedLimit = threadpool->maxThreadCount();
+ threadpool->setMaxThreadCount(limit);
+
+ // reserve up to the limit
+ for (int i = 0; i < limit; ++i)
+ threadpool->reserveThread();
+
+ // reserveThread() should always reserve a thread, regardless of
+ // how many have been previously reserved
+ threadpool->reserveThread();
+ QCOMPARE(threadpool->activeThreadCount(), (limit > 0 ? limit : 0) + 1);
+ threadpool->reserveThread();
+ QCOMPARE(threadpool->activeThreadCount(), (limit > 0 ? limit : 0) + 2);
+
+ // cleanup
+ threadpool->releaseThread();
+ threadpool->releaseThread();
+ for (int i = 0; i < limit; ++i)
+ threadpool->releaseThread();
+
+ // reserving threads in children should not effect the parent
+ {
+ QThreadPool threadpool2(threadpool);
+ threadpool2.setMaxThreadCount(limit);
+
+ // reserve up to the limit
+ for (int i = 0; i < limit; ++i)
+ threadpool2.reserveThread();
+
+ // reserveThread() should always reserve a thread, regardless
+ // of how many have been previously reserved
+ threadpool2.reserveThread();
+ QCOMPARE(threadpool2.activeThreadCount(), (limit > 0 ? limit : 0) + 1);
+ threadpool2.reserveThread();
+ QCOMPARE(threadpool2.activeThreadCount(), (limit > 0 ? limit : 0) + 2);
+
+ threadpool->reserveThread();
+ QCOMPARE(threadpool->activeThreadCount(), 1);
+ threadpool->reserveThread();
+ QCOMPARE(threadpool->activeThreadCount(), 2);
+
+ // cleanup
+ threadpool2.releaseThread();
+ threadpool2.releaseThread();
+ threadpool->releaseThread();
+ threadpool->releaseThread();
+ while (threadpool2.activeThreadCount() > 0)
+ threadpool2.releaseThread();
+ }
+
+ // reset limit on global QThreadPool
+ threadpool->setMaxThreadCount(savedLimit);
+}
+
+void tst_QThreadPool::releaseThread_data()
+{
+ setMaxThreadCount_data();
+}
+
+void tst_QThreadPool::releaseThread()
+{
+ QFETCH(int, limit);
+ QThreadPool *threadpool = QThreadPool::globalInstance();
+ int savedLimit = threadpool->maxThreadCount();
+ threadpool->setMaxThreadCount(limit);
+
+ // reserve up to the limit
+ for (int i = 0; i < limit; ++i)
+ threadpool->reserveThread();
+
+ // release should decrease the number of reserved threads
+ int reserved = threadpool->activeThreadCount();
+ while (reserved-- > 0) {
+ threadpool->releaseThread();
+ QCOMPARE(threadpool->activeThreadCount(), reserved);
+ }
+ QCOMPARE(threadpool->activeThreadCount(), 0);
+
+ // releaseThread() can release more than have been reserved
+ threadpool->releaseThread();
+ QCOMPARE(threadpool->activeThreadCount(), -1);
+ threadpool->reserveThread();
+ QCOMPARE(threadpool->activeThreadCount(), 0);
+
+ // releasing threads in children should not effect the parent
+ {
+ QThreadPool threadpool2(threadpool);
+ threadpool2.setMaxThreadCount(limit);
+
+ // reserve up to the limit
+ for (int i = 0; i < limit; ++i)
+ threadpool2.reserveThread();
+
+ // release should decrease the number of reserved threads
+ int reserved = threadpool2.activeThreadCount();
+ while (reserved-- > 0) {
+ threadpool2.releaseThread();
+ QCOMPARE(threadpool2.activeThreadCount(), reserved);
+ QCOMPARE(threadpool->activeThreadCount(), 0);
+ }
+ QCOMPARE(threadpool2.activeThreadCount(), 0);
+ QCOMPARE(threadpool->activeThreadCount(), 0);
+
+ // releaseThread() can release more than have been reserved
+ threadpool2.releaseThread();
+ QCOMPARE(threadpool2.activeThreadCount(), -1);
+ QCOMPARE(threadpool->activeThreadCount(), 0);
+ threadpool2.reserveThread();
+ QCOMPARE(threadpool2.activeThreadCount(), 0);
+ QCOMPARE(threadpool->activeThreadCount(), 0);
+ }
+
+ // reset limit on global QThreadPool
+ threadpool->setMaxThreadCount(savedLimit);
+}
+
+QAtomicInt count;
+class CountingRunnable : public QRunnable
+{
+ public: void run()
+ {
+ count.ref();
+ }
+};
+
+void tst_QThreadPool::start()
+{
+ const int runs = 1000;
+ count = 0;
+ {
+ QThreadPool threadPool;
+ for (int i = 0; i< runs; ++i) {
+ threadPool.start(new CountingRunnable());
+ }
+ }
+ QCOMPARE(int(count), runs);
+}
+
+void tst_QThreadPool::tryStart()
+{
+ class WaitingTask : public QRunnable
+ {
+ public:
+ QSemaphore semaphore;
+
+ WaitingTask() { setAutoDelete(false); }
+
+ void run()
+ {
+ semaphore.acquire();
+ count.ref();
+ }
+ };
+
+ count = 0;
+
+ WaitingTask task;
+ QThreadPool threadPool;
+ for (int i = 0; i < threadPool.maxThreadCount(); ++i) {
+ threadPool.start(&task);
+ }
+ QVERIFY(!threadPool.tryStart(&task));
+ task.semaphore.release(threadPool.maxThreadCount());
+ threadPool.waitForDone();
+ QCOMPARE(int(count), threadPool.maxThreadCount());
+}
+
+QMutex mutex;
+int activeThreads = 0;
+int peakActiveThreads = 0;
+void tst_QThreadPool::tryStartPeakThreadCount()
+{
+ class CounterTask : public QRunnable
+ {
+ public:
+ CounterTask() { setAutoDelete(false); }
+
+ void run()
+ {
+ {
+ QMutexLocker lock(&mutex);
+ ++activeThreads;
+ peakActiveThreads = qMax(peakActiveThreads, activeThreads);
+ }
+
+ QTest::qWait(100);
+ {
+ QMutexLocker lock(&mutex);
+ --activeThreads;
+ }
+ }
+ };
+
+ CounterTask task;
+ QThreadPool threadPool;
+
+ for (int i = 0; i < 20; ++i) {
+ if (threadPool.tryStart(&task) == false)
+ QTest::qWait(10);
+ }
+ QCOMPARE(peakActiveThreads, QThread::idealThreadCount());
+
+ for (int i = 0; i < 20; ++i) {
+ if (threadPool.tryStart(&task) == false)
+ QTest::qWait(10);
+ }
+ QCOMPARE(peakActiveThreads, QThread::idealThreadCount());
+}
+
+void tst_QThreadPool::tryStartCount()
+{
+ class SleeperTask : public QRunnable
+ {
+ public:
+ SleeperTask() { setAutoDelete(false); }
+
+ void run()
+ {
+ QTest::qWait(50);
+ }
+ };
+
+ SleeperTask task;
+ QThreadPool threadPool;
+ const int runs = 5;
+
+ for (int i = 0; i < runs; ++i) {
+// qDebug() << "iteration" << i;
+ int count = 0;
+ while (threadPool.tryStart(&task))
+ ++count;
+ QCOMPARE(count, QThread::idealThreadCount());
+
+ QTest::qWait(100);
+ }
+}
+
+void tst_QThreadPool::waitForDone()
+{
+ QTime total, pass;
+ total.start();
+
+ QThreadPool threadPool;
+ while (total.elapsed() < 10000) {
+ int runs;
+ runs = count = 0;
+ pass.restart();
+ while (pass.elapsed() < 100) {
+ threadPool.start(new CountingRunnable());
+ ++runs;
+ }
+ threadPool.waitForDone();
+ QCOMPARE(int(count), runs);
+
+ runs = count = 0;
+ pass.restart();
+ while (pass.elapsed() < 100) {
+ threadPool.start(new CountingRunnable());
+ threadPool.start(new CountingRunnable());
+ runs += 2;
+ }
+ threadPool.waitForDone();
+ QCOMPARE(int(count), runs);
+ }
+}
+
+void tst_QThreadPool::destroyingWaitsForTasksToFinish()
+{
+ QTime total, pass;
+ total.start();
+
+ while (total.elapsed() < 10000) {
+ int runs;
+ runs = count = 0;
+ {
+ QThreadPool threadPool;
+ pass.restart();
+ while (pass.elapsed() < 100) {
+ threadPool.start(new CountingRunnable());
+ ++runs;
+ }
+ }
+ QCOMPARE(int(count), runs);
+
+ runs = count = 0;
+ {
+ QThreadPool threadPool;
+ pass.restart();
+ while (pass.elapsed() < 100) {
+ threadPool.start(new CountingRunnable());
+ threadPool.start(new CountingRunnable());
+ runs += 2;
+ }
+ }
+ QCOMPARE(int(count), runs);
+ }
+}
+
+void tst_QThreadPool::stressTest()
+{
+ class Task : public QRunnable
+ {
+ QSemaphore semaphore;
+ public:
+ Task() { setAutoDelete(false); }
+
+ void start()
+ {
+ QThreadPool::globalInstance()->start(this);
+ }
+
+ void wait()
+ {
+ semaphore.acquire();
+ }
+
+ void run()
+ {
+ semaphore.release();
+ }
+ };
+
+ QTime total;
+ total.start();
+ while (total.elapsed() < 30000) {
+ Task t;
+ t.start();
+ t.wait();
+ }
+}
+
+QTEST_MAIN(tst_QThreadPool);
+#include "tst_qthreadpool.moc"