summaryrefslogtreecommitdiffstats
path: root/tests/auto/qthreadpool
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /tests/auto/qthreadpool
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'tests/auto/qthreadpool')
-rw-r--r--tests/auto/qthreadpool/.gitignore1
-rw-r--r--tests/auto/qthreadpool/qthreadpool.pro4
-rw-r--r--tests/auto/qthreadpool/tst_qthreadpool.cpp870
3 files changed, 875 insertions, 0 deletions
diff --git a/tests/auto/qthreadpool/.gitignore b/tests/auto/qthreadpool/.gitignore
new file mode 100644
index 0000000000..16105821a2
--- /dev/null
+++ b/tests/auto/qthreadpool/.gitignore
@@ -0,0 +1 @@
+tst_qthreadpool
diff --git a/tests/auto/qthreadpool/qthreadpool.pro b/tests/auto/qthreadpool/qthreadpool.pro
new file mode 100644
index 0000000000..dbaeb208dd
--- /dev/null
+++ b/tests/auto/qthreadpool/qthreadpool.pro
@@ -0,0 +1,4 @@
+load(qttest_p4)
+SOURCES += tst_qthreadpool.cpp
+QT = core
+CONFIG += parallel_test
diff --git a/tests/auto/qthreadpool/tst_qthreadpool.cpp b/tests/auto/qthreadpool/tst_qthreadpool.cpp
new file mode 100644
index 0000000000..165e3f87ba
--- /dev/null
+++ b/tests/auto/qthreadpool/tst_qthreadpool.cpp
@@ -0,0 +1,870 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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 waitForDoneTimeout();
+ 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)
+ QTest::qSleep(100); // no busy loop - this doesn't work with FIFO schedulers
+}
+
+/*
+ Test running via QThreadPool::globalInstance()
+*/
+void tst_QThreadPool::singleton()
+{
+ ran = false;
+ QThreadPool::globalInstance()->start(new TestTask());
+ while (ran == false)
+ QTest::qSleep(100); // no busy loop - this doesn't work with FIFO schedulers
+}
+
+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::waitForDoneTimeout()
+{
+ class BlockedTask : public QRunnable
+ {
+ public:
+ QMutex mutex;
+ BlockedTask() { setAutoDelete(false); }
+
+ void run()
+ {
+ mutex.lock();
+ mutex.unlock();
+ QTest::qSleep(50);
+ }
+ };
+
+ QThreadPool threadPool;
+
+ BlockedTask *task = new BlockedTask;
+ task->mutex.lock();
+ threadPool.start(task);
+ QVERIFY(!threadPool.waitForDone(100));
+ task->mutex.unlock();
+ QVERIFY(threadPool.waitForDone(400));
+}
+
+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"