summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/thread/qthread
diff options
context:
space:
mode:
authorHolger Ihrig <holger.ihrig@nokia.com>2011-08-26 12:56:14 +0200
committerHolger Ihrig <holger.ihrig@nokia.com>2011-09-01 12:54:58 +0200
commit82e715b2770258fa2c536aeae1f46c7fccdcdecf (patch)
treea4b9135effe2109b164f622314a6ffac6198a8f8 /tests/auto/corelib/thread/qthread
parentc9a5ccb268b5e2b2ce0743989c44f808b538ba9b (diff)
Moving relevant tests to corelib/thread
Task-number: QTBUG-21066 Change-Id: Ia16fa8961f1a73f4da6709197b5dd9929c16583f Reviewed-on: http://codereview.qt.nokia.com/3663 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Rohan McGovern <rohan.mcgovern@nokia.com> Reviewed-by: Jason McDonald <jason.mcdonald@nokia.com>
Diffstat (limited to 'tests/auto/corelib/thread/qthread')
-rw-r--r--tests/auto/corelib/thread/qthread/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qthread/qthread.pro5
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp1249
3 files changed, 1255 insertions, 0 deletions
diff --git a/tests/auto/corelib/thread/qthread/.gitignore b/tests/auto/corelib/thread/qthread/.gitignore
new file mode 100644
index 0000000000..4413a75588
--- /dev/null
+++ b/tests/auto/corelib/thread/qthread/.gitignore
@@ -0,0 +1 @@
+tst_qthread
diff --git a/tests/auto/corelib/thread/qthread/qthread.pro b/tests/auto/corelib/thread/qthread/qthread.pro
new file mode 100644
index 0000000000..d3b1028034
--- /dev/null
+++ b/tests/auto/corelib/thread/qthread/qthread.pro
@@ -0,0 +1,5 @@
+load(qttest_p4)
+SOURCES += tst_qthread.cpp
+QT = core
+symbian:LIBS += -llibpthread
+CONFIG += parallel_test
diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
new file mode 100644
index 0000000000..3c46212c16
--- /dev/null
+++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp
@@ -0,0 +1,1249 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** 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.
+**
+** 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qtimer.h>
+#include <qwaitcondition.h>
+#include <qdebug.h>
+#include <qmetaobject.h>
+
+#ifdef Q_OS_UNIX
+#include <pthread.h>
+#endif
+#if defined(Q_OS_WINCE)
+#include <windows.h>
+#elif defined(Q_OS_WIN)
+#include <process.h>
+#include <windows.h>
+#endif
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QThread : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QThread();
+ virtual ~tst_QThread();
+
+private slots:
+ void currentThreadId();
+ void currentThread();
+ void idealThreadCount();
+ void isFinished();
+ void isRunning();
+ void setPriority();
+ void priority();
+ void setStackSize();
+ void stackSize();
+ void exit();
+ void start();
+ void terminate();
+ void quit();
+ void wait();
+ void started();
+ void finished();
+ void terminated();
+ void run();
+ void exec();
+ void setTerminationEnabled();
+ void sleep();
+ void msleep();
+ void usleep();
+
+ void nativeThreadAdoption();
+ void adoptedThreadAffinity();
+ void adoptedThreadSetPriority();
+ void adoptedThreadExit();
+ void adoptedThreadExec();
+ void adoptedThreadFinished();
+ void adoptedThreadExecFinished();
+ void adoptMultipleThreads();
+ void adoptMultipleThreadsOverlap();
+
+ void QTBUG13810_exitAndStart();
+ void QTBUG15378_exitAndExec();
+
+ void connectThreadFinishedSignalToObjectDeleteLaterSlot();
+ void wait2();
+ void wait3_slowDestructor();
+ void destroyFinishRace();
+ void startFinishRace();
+ void startAndQuitCustomEventLoop();
+
+ void stressTest();
+};
+
+enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute };
+
+class SignalRecorder : public QObject
+{
+ Q_OBJECT
+public:
+ QAtomicInt activationCount;
+
+ inline SignalRecorder()
+ { activationCount = 0; }
+
+ bool wasActivated()
+ { return activationCount > 0; }
+
+public slots:
+ void slot();
+};
+
+void SignalRecorder::slot()
+{ activationCount.ref(); }
+
+class Current_Thread : public QThread
+{
+public:
+ Qt::HANDLE id;
+ QThread *thread;
+
+ void run()
+ {
+ id = QThread::currentThreadId();
+ thread = QThread::currentThread();
+ }
+};
+
+class Simple_Thread : public QThread
+{
+public:
+ QMutex mutex;
+ QWaitCondition cond;
+
+ void run()
+ {
+ QMutexLocker locker(&mutex);
+ cond.wakeOne();
+ }
+};
+
+class Exit_Object : public QObject
+{
+ Q_OBJECT
+public:
+ QThread *thread;
+ int code;
+public slots:
+ void slot()
+ { thread->exit(code); }
+};
+
+class Exit_Thread : public Simple_Thread
+{
+public:
+ Exit_Object *object;
+ int code;
+ int result;
+
+ void run()
+ {
+ Simple_Thread::run();
+ if (object) {
+ object->thread = this;
+ object->code = code;
+ QTimer::singleShot(100, object, SLOT(slot()));
+ }
+ result = exec();
+ }
+};
+
+class Terminate_Thread : public Simple_Thread
+{
+public:
+ void run()
+ {
+ setTerminationEnabled(false);
+ {
+ QMutexLocker locker(&mutex);
+ cond.wakeOne();
+ cond.wait(&mutex, five_minutes);
+ }
+ setTerminationEnabled(true);
+ qFatal("tst_QThread: test case hung");
+ }
+};
+
+class Quit_Object : public QObject
+{
+ Q_OBJECT
+public:
+ QThread *thread;
+public slots:
+ void slot()
+ { thread->quit(); }
+};
+
+class Quit_Thread : public Simple_Thread
+{
+public:
+ Quit_Object *object;
+ int result;
+
+ void run()
+ {
+ Simple_Thread::run();
+ if (object) {
+ object->thread = this;
+ QTimer::singleShot(100, object, SLOT(slot()));
+ }
+ result = exec();
+ }
+};
+
+class Sleep_Thread : public Simple_Thread
+{
+public:
+ enum SleepType { Second, Millisecond, Microsecond };
+
+ SleepType sleepType;
+ int interval;
+
+ int elapsed; // result, in *MILLISECONDS*
+
+ void run()
+ {
+ QMutexLocker locker(&mutex);
+
+ elapsed = 0;
+ QTime time;
+ time.start();
+ switch (sleepType) {
+ case Second:
+ sleep(interval);
+ break;
+ case Millisecond:
+ msleep(interval);
+ break;
+ case Microsecond:
+ usleep(interval);
+ break;
+ }
+ elapsed = time.elapsed();
+
+ cond.wakeOne();
+ }
+};
+
+tst_QThread::tst_QThread()
+
+{
+}
+
+tst_QThread::~tst_QThread()
+{
+
+}
+
+void tst_QThread::currentThreadId()
+{
+ Current_Thread thread;
+ thread.id = 0;
+ thread.thread = 0;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.id != 0);
+ QVERIFY(thread.id != QThread::currentThreadId());
+}
+
+void tst_QThread::currentThread()
+{
+ QVERIFY(QThread::currentThread() != 0);
+ QCOMPARE(QThread::currentThread(), thread());
+
+ Current_Thread thread;
+ thread.id = 0;
+ thread.thread = 0;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QCOMPARE(thread.thread, (QThread *)&thread);
+}
+
+void tst_QThread::idealThreadCount()
+{
+ QVERIFY(QThread::idealThreadCount() > 0);
+ qDebug() << "Available cpu cores:" << QThread::idealThreadCount();
+}
+
+void tst_QThread::isFinished()
+{
+ Simple_Thread thread;
+ QVERIFY(!thread.isFinished());
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(!thread.isFinished());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.isFinished());
+}
+
+void tst_QThread::isRunning()
+{
+ Simple_Thread thread;
+ QVERIFY(!thread.isRunning());
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(thread.isRunning());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(!thread.isRunning());
+}
+
+void tst_QThread::setPriority()
+{
+ Simple_Thread thread;
+
+ // cannot change the priority, since the thread is not running
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::IdlePriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::LowestPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::LowPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::NormalPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::HighPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::HighestPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::TimeCriticalPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+
+ // change the priority of a running thread
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ thread.setPriority(QThread::IdlePriority);
+ QCOMPARE(thread.priority(), QThread::IdlePriority);
+ thread.setPriority(QThread::LowestPriority);
+ QCOMPARE(thread.priority(), QThread::LowestPriority);
+ thread.setPriority(QThread::LowPriority);
+ QCOMPARE(thread.priority(), QThread::LowPriority);
+ thread.setPriority(QThread::NormalPriority);
+ QCOMPARE(thread.priority(), QThread::NormalPriority);
+ thread.setPriority(QThread::HighPriority);
+ QCOMPARE(thread.priority(), QThread::HighPriority);
+ thread.setPriority(QThread::HighestPriority);
+ QCOMPARE(thread.priority(), QThread::HighestPriority);
+ thread.setPriority(QThread::TimeCriticalPriority);
+ QCOMPARE(thread.priority(), QThread::TimeCriticalPriority);
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::IdlePriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::LowestPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::LowPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::NormalPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::HighPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::HighestPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::TimeCriticalPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+}
+
+void tst_QThread::priority()
+{ DEPENDS_ON("setPriority"); }
+
+void tst_QThread::setStackSize()
+{
+ Simple_Thread thread;
+ QCOMPARE(thread.stackSize(), 0u);
+ thread.setStackSize(8192u);
+ QCOMPARE(thread.stackSize(), 8192u);
+ thread.setStackSize(0u);
+ QCOMPARE(thread.stackSize(), 0u);
+}
+
+void tst_QThread::stackSize()
+{
+ DEPENDS_ON("setStackSize");
+}
+
+void tst_QThread::exit()
+{
+ Exit_Thread thread;
+ thread.object = new Exit_Object;
+ thread.object->moveToThread(&thread);
+ thread.code = 42;
+ thread.result = 0;
+ QVERIFY(!thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(thread.isRunning());
+ QVERIFY(!thread.isFinished());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QCOMPARE(thread.result, thread.code);
+ delete thread.object;
+
+ Exit_Thread thread2;
+ thread2.object = 0;
+ thread2.code = 53;
+ thread2.result = 0;
+ QMutexLocker locker2(&thread2.mutex);
+ thread2.start();
+ thread2.exit(thread2.code);
+ thread2.cond.wait(locker2.mutex());
+ QVERIFY(thread2.wait(five_minutes));
+ QCOMPARE(thread2.result, thread2.code);
+}
+
+void tst_QThread::start()
+{
+ QThread::Priority priorities[] = {
+ QThread::IdlePriority,
+ QThread::LowestPriority,
+ QThread::LowPriority,
+ QThread::NormalPriority,
+ QThread::HighPriority,
+ QThread::HighestPriority,
+ QThread::TimeCriticalPriority,
+ QThread::InheritPriority
+ };
+ const int prio_count = sizeof(priorities) / sizeof(QThread::Priority);
+
+ for (int i = 0; i < prio_count; ++i) {
+ Simple_Thread thread;
+ QVERIFY(!thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QMutexLocker locker(&thread.mutex);
+ thread.start(priorities[i]);
+ QVERIFY(thread.isRunning());
+ QVERIFY(!thread.isFinished());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ }
+}
+
+void tst_QThread::terminate()
+{
+ Terminate_Thread thread;
+ {
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(thread.cond.wait(locker.mutex(), five_minutes));
+ thread.terminate();
+ thread.cond.wakeOne();
+ }
+ QVERIFY(thread.wait(five_minutes));
+}
+
+void tst_QThread::quit()
+{
+ Quit_Thread thread;
+ thread.object = new Quit_Object;
+ thread.object->moveToThread(&thread);
+ thread.result = -1;
+ QVERIFY(!thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(thread.isRunning());
+ QVERIFY(!thread.isFinished());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QCOMPARE(thread.result, 0);
+ delete thread.object;
+
+ Quit_Thread thread2;
+ thread2.object = 0;
+ thread2.result = -1;
+ QMutexLocker locker2(&thread2.mutex);
+ thread2.start();
+ thread2.quit();
+ thread2.cond.wait(locker2.mutex());
+ QVERIFY(thread2.wait(five_minutes));
+ QCOMPARE(thread2.result, 0);
+}
+
+void tst_QThread::wait()
+{
+ DEPENDS_ON("isRunning");
+ DEPENDS_ON("isFinished");
+}
+
+void tst_QThread::started()
+{
+ SignalRecorder recorder;
+ Simple_Thread thread;
+ connect(&thread, SIGNAL(started()), &recorder, SLOT(slot()), Qt::DirectConnection);
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::finished()
+{
+ SignalRecorder recorder;
+ Simple_Thread thread;
+ connect(&thread, SIGNAL(finished()), &recorder, SLOT(slot()), Qt::DirectConnection);
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::terminated()
+{
+ SignalRecorder recorder;
+ Terminate_Thread thread;
+ connect(&thread, SIGNAL(terminated()), &recorder, SLOT(slot()), Qt::DirectConnection);
+ {
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ thread.cond.wait(locker.mutex());
+ thread.terminate();
+ thread.cond.wakeOne();
+ }
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::run()
+{ DEPENDS_ON("wait()"); }
+
+void tst_QThread::exec()
+{
+ DEPENDS_ON("exit()");
+ DEPENDS_ON("quit()");
+
+ class MultipleExecThread : public QThread
+ {
+ public:
+ int res1, res2;
+
+ MultipleExecThread() : res1(-2), res2(-2) { }
+
+ void run()
+ {
+ {
+ Exit_Object o;
+ o.thread = this;
+ o.code = 1;
+ QTimer::singleShot(100, &o, SLOT(slot()));
+ res1 = exec();
+ }
+ {
+ Exit_Object o;
+ o.thread = this;
+ o.code = 2;
+ QTimer::singleShot(100, &o, SLOT(slot()));
+ res2 = exec();
+ }
+ }
+ };
+
+ MultipleExecThread thread;
+ thread.start();
+ QVERIFY(thread.wait());
+
+ QCOMPARE(thread.res1, 1);
+ QCOMPARE(thread.res2, 2);
+}
+
+void tst_QThread::setTerminationEnabled()
+{ DEPENDS_ON("terminate"); }
+
+void tst_QThread::sleep()
+{
+ Sleep_Thread thread;
+ thread.sleepType = Sleep_Thread::Second;
+ thread.interval = 2;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.elapsed >= 2000);
+}
+
+void tst_QThread::msleep()
+{
+ Sleep_Thread thread;
+ thread.sleepType = Sleep_Thread::Millisecond;
+ thread.interval = 120;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+#if defined (Q_OS_WIN)
+ // Since the resolution of QTime is so coarse...
+ QVERIFY(thread.elapsed >= 100);
+#else
+ QVERIFY(thread.elapsed >= 120);
+#endif
+}
+
+void tst_QThread::usleep()
+{
+ Sleep_Thread thread;
+ thread.sleepType = Sleep_Thread::Microsecond;
+ thread.interval = 120000;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+#if defined (Q_OS_WIN)
+ // Since the resolution of QTime is so coarse...
+ QVERIFY(thread.elapsed >= 100);
+#else
+ QVERIFY(thread.elapsed >= 120);
+#endif
+}
+
+typedef void (*FunctionPointer)(void *);
+void noop(void*) { }
+
+#ifdef Q_OS_SYMBIAN
+typedef RThread ThreadHandle;
+#elif defined Q_OS_UNIX
+ typedef pthread_t ThreadHandle;
+#elif defined Q_OS_WIN
+ typedef HANDLE ThreadHandle;
+#endif
+
+#ifdef Q_OS_WIN
+#define WIN_FIX_STDCALL __stdcall
+#else
+#define WIN_FIX_STDCALL
+#endif
+
+class NativeThreadWrapper
+{
+public:
+ NativeThreadWrapper() : qthread(0), waitForStop(false) {}
+ void start(FunctionPointer functionPointer = noop, void *data = 0);
+ void startAndWait(FunctionPointer functionPointer = noop, void *data = 0);
+ void join();
+ void setWaitForStop() { waitForStop = true; }
+ void stop();
+
+ ThreadHandle nativeThreadHandle;
+ QThread *qthread;
+ QWaitCondition startCondition;
+ QMutex mutex;
+ bool waitForStop;
+ QWaitCondition stopCondition;
+protected:
+ static void *runUnix(void *data);
+ static unsigned WIN_FIX_STDCALL runWin(void *data);
+ static int runSymbian(void *data);
+
+ FunctionPointer functionPointer;
+ void *data;
+};
+
+void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data)
+{
+ this->functionPointer = functionPointer;
+ this->data = data;
+#ifdef Q_OS_SYMBIAN
+ qt_symbian_throwIfError(nativeThreadHandle.Create(KNullDesC(), NativeThreadWrapper::runSymbian, 1024, &User::Allocator(), this));
+ nativeThreadHandle.Resume();
+#elif defined Q_OS_UNIX
+ const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this);
+ Q_UNUSED(state);
+#elif defined(Q_OS_WINCE)
+ nativeThreadHandle = CreateThread(NULL, 0 , (LPTHREAD_START_ROUTINE)NativeThreadWrapper::runWin , this, 0, NULL);
+#elif defined Q_OS_WIN
+ unsigned thrdid = 0;
+ nativeThreadHandle = (Qt::HANDLE) _beginthreadex(NULL, 0, NativeThreadWrapper::runWin, this, 0, &thrdid);
+#endif
+}
+
+void NativeThreadWrapper::startAndWait(FunctionPointer functionPointer, void *data)
+{
+ QMutexLocker locker(&mutex);
+ start(functionPointer, data);
+ startCondition.wait(locker.mutex());
+}
+
+void NativeThreadWrapper::join()
+{
+#ifdef Q_OS_SYMBIAN
+ TRequestStatus stat;
+ nativeThreadHandle.Logon(stat);
+ User::WaitForRequest(stat);
+ nativeThreadHandle.Close();
+#elif defined Q_OS_UNIX
+ pthread_join(nativeThreadHandle, 0);
+#elif defined Q_OS_WIN
+ WaitForSingleObject(nativeThreadHandle, INFINITE);
+ CloseHandle(nativeThreadHandle);
+#endif
+}
+
+void *NativeThreadWrapper::runUnix(void *that)
+{
+ NativeThreadWrapper *nativeThreadWrapper = reinterpret_cast<NativeThreadWrapper*>(that);
+
+ // Adopt thread, create QThread object.
+ nativeThreadWrapper->qthread = QThread::currentThread();
+
+ // Release main thread.
+ {
+ QMutexLocker lock(&nativeThreadWrapper->mutex);
+ nativeThreadWrapper->startCondition.wakeOne();
+ }
+
+ // Run function.
+ nativeThreadWrapper->functionPointer(nativeThreadWrapper->data);
+
+ // Wait for stop.
+ {
+ QMutexLocker lock(&nativeThreadWrapper->mutex);
+ if (nativeThreadWrapper->waitForStop)
+ nativeThreadWrapper->stopCondition.wait(lock.mutex());
+ }
+
+ return 0;
+}
+
+unsigned WIN_FIX_STDCALL NativeThreadWrapper::runWin(void *data)
+{
+ runUnix(data);
+ return 0;
+}
+
+int NativeThreadWrapper::runSymbian(void *data)
+{
+ runUnix(data);
+ return 0;
+}
+
+void NativeThreadWrapper::stop()
+{
+ QMutexLocker lock(&mutex);
+ waitForStop = false;
+ stopCondition.wakeOne();
+}
+
+bool threadAdoptedOk = false;
+QThread *mainThread;
+void testNativeThreadAdoption(void *)
+{
+ threadAdoptedOk = (QThread::currentThreadId() != 0
+ && QThread::currentThread() != 0
+ && QThread::currentThread() != mainThread);
+}
+void tst_QThread::nativeThreadAdoption()
+{
+ threadAdoptedOk = false;
+ mainThread = QThread::currentThread();
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+ nativeThread.startAndWait(testNativeThreadAdoption);
+ QVERIFY(nativeThread.qthread);
+
+ nativeThread.stop();
+ nativeThread.join();
+
+ QVERIFY(threadAdoptedOk);
+}
+
+void adoptedThreadAffinityFunction(void *arg)
+{
+ QThread **affinity = reinterpret_cast<QThread **>(arg);
+ QThread *current = QThread::currentThread();
+ affinity[0] = current;
+ affinity[1] = current->thread();
+}
+
+void tst_QThread::adoptedThreadAffinity()
+{
+ QThread *affinity[2] = { 0, 0 };
+
+ NativeThreadWrapper thread;
+ thread.startAndWait(adoptedThreadAffinityFunction, affinity);
+ thread.join();
+
+ // adopted thread should have affinity to itself
+ QCOMPARE(affinity[0], affinity[1]);
+}
+
+void tst_QThread::adoptedThreadSetPriority()
+{
+
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+ nativeThread.startAndWait();
+
+ // change the priority of a running thread
+ QCOMPARE(nativeThread.qthread->priority(), QThread::InheritPriority);
+ nativeThread.qthread->setPriority(QThread::IdlePriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::IdlePriority);
+ nativeThread.qthread->setPriority(QThread::LowestPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::LowestPriority);
+ nativeThread.qthread->setPriority(QThread::LowPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::LowPriority);
+ nativeThread.qthread->setPriority(QThread::NormalPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::NormalPriority);
+ nativeThread.qthread->setPriority(QThread::HighPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::HighPriority);
+ nativeThread.qthread->setPriority(QThread::HighestPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::HighestPriority);
+ nativeThread.qthread->setPriority(QThread::TimeCriticalPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::TimeCriticalPriority);
+
+ nativeThread.stop();
+ nativeThread.join();
+}
+
+void tst_QThread::adoptedThreadExit()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+
+ nativeThread.startAndWait();
+ QVERIFY(nativeThread.qthread);
+ QVERIFY(nativeThread.qthread->isRunning());
+ QVERIFY(!nativeThread.qthread->isFinished());
+
+ nativeThread.stop();
+ nativeThread.join();
+}
+
+void adoptedThreadExecFunction(void *)
+{
+ QThread * const adoptedThread = QThread::currentThread();
+ QEventLoop eventLoop(adoptedThread);
+
+ const int code = 1;
+ Exit_Object o;
+ o.thread = adoptedThread;
+ o.code = code;
+ QTimer::singleShot(100, &o, SLOT(slot()));
+
+ const int result = eventLoop.exec();
+ QCOMPARE(result, code);
+}
+
+void tst_QThread::adoptedThreadExec()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.start(adoptedThreadExecFunction);
+ nativeThread.join();
+}
+
+/*
+ Test that you get the finished signal when an adopted thread exits.
+*/
+void tst_QThread::adoptedThreadFinished()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+ nativeThread.startAndWait();
+
+ QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ nativeThread.stop();
+ nativeThread.join();
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
+void tst_QThread::adoptedThreadExecFinished()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+ nativeThread.startAndWait(adoptedThreadExecFunction);
+
+ QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ nativeThread.stop();
+ nativeThread.join();
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
+void tst_QThread::adoptMultipleThreads()
+{
+#if defined(Q_OS_WIN)
+ // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already.
+# if defined(Q_OS_WINCE)
+ const int numThreads = 20;
+# else
+ // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher()
+ const int numThreads = 200;
+# endif
+#else
+ const int numThreads = 5;
+#endif
+ QVector<NativeThreadWrapper*> nativeThreads;
+
+ SignalRecorder recorder;
+
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.append(new NativeThreadWrapper());
+ nativeThreads.at(i)->setWaitForStop();
+ nativeThreads.at(i)->startAndWait();
+ QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot()));
+ }
+
+ QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.at(i)->stop();
+ nativeThreads.at(i)->join();
+ delete nativeThreads.at(i);
+ }
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QCOMPARE(int(recorder.activationCount), numThreads);
+}
+
+void tst_QThread::adoptMultipleThreadsOverlap()
+{
+#if defined(Q_OS_WIN)
+ // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already.
+# if defined(Q_OS_WINCE)
+ const int numThreads = 20;
+# else
+ // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher()
+ const int numThreads = 200;
+# endif
+#elif defined(Q_OS_SYMBIAN)
+ // stress the monitoring thread's add function
+ const int numThreads = 100;
+#else
+ const int numThreads = 5;
+#endif
+ QVector<NativeThreadWrapper*> nativeThreads;
+
+ SignalRecorder recorder;
+
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.append(new NativeThreadWrapper());
+ nativeThreads.at(i)->setWaitForStop();
+ nativeThreads.at(i)->mutex.lock();
+ nativeThreads.at(i)->start();
+ }
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.at(i)->startCondition.wait(&nativeThreads.at(i)->mutex);
+ QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot()));
+ nativeThreads.at(i)->mutex.unlock();
+ }
+
+ QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.at(i)->stop();
+ nativeThreads.at(i)->join();
+ delete nativeThreads.at(i);
+ }
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QCOMPARE(int(recorder.activationCount), numThreads);
+}
+void tst_QThread::stressTest()
+{
+#if defined(Q_OS_WINCE)
+ QSKIP("Disconnects on WinCE, skipping...", SkipAll);
+#endif
+ QTime t;
+ t.start();
+ while (t.elapsed() < one_minute) {
+ Current_Thread t;
+ t.start();
+ t.wait(one_minute);
+ }
+}
+
+class Syncronizer : public QObject
+{ Q_OBJECT
+public slots:
+ void setProp(int p) {
+ if(m_prop != p) {
+ m_prop = p;
+ emit propChanged(p);
+ }
+ }
+signals:
+ void propChanged(int);
+public:
+ Syncronizer() : m_prop(42) {}
+ int m_prop;
+};
+
+void tst_QThread::QTBUG13810_exitAndStart()
+{
+ QThread thread;
+ thread.exit(555); //should do nothing
+
+ thread.start();
+
+ //test that the thread is running by executing queued connected signal there
+ Syncronizer sync1;
+ sync1.moveToThread(&thread);
+ Syncronizer sync2;
+ sync2.moveToThread(&thread);
+ connect(&sync2, SIGNAL(propChanged(int)), &sync1, SLOT(setProp(int)), Qt::QueuedConnection);
+ connect(&sync1, SIGNAL(propChanged(int)), &thread, SLOT(quit()), Qt::QueuedConnection);
+ QMetaObject::invokeMethod(&sync2, "setProp", Qt::QueuedConnection , Q_ARG(int, 89));
+ QTest::qWait(50);
+ while(!thread.wait(10))
+ QTest::qWait(10);
+ QCOMPARE(sync2.m_prop, 89);
+ QCOMPARE(sync1.m_prop, 89);
+}
+
+void tst_QThread::QTBUG15378_exitAndExec()
+{
+ class Thread : public QThread {
+ public:
+ QSemaphore sem1;
+ QSemaphore sem2;
+ volatile int value;
+ void run() {
+ sem1.acquire();
+ value = exec(); //First entrence
+ sem2.release();
+ value = exec(); // Second loop
+ }
+ };
+ Thread thread;
+ thread.value = 0;
+ thread.start();
+ thread.exit(556);
+ thread.sem1.release(); //should exit the first loop
+ thread.sem2.acquire();
+ int v = thread.value;
+ QCOMPARE(v, 556);
+
+ //test that the thread is running by executing queued connected signal there
+ Syncronizer sync1;
+ sync1.moveToThread(&thread);
+ Syncronizer sync2;
+ sync2.moveToThread(&thread);
+ connect(&sync2, SIGNAL(propChanged(int)), &sync1, SLOT(setProp(int)), Qt::QueuedConnection);
+ connect(&sync1, SIGNAL(propChanged(int)), &thread, SLOT(quit()), Qt::QueuedConnection);
+ QMetaObject::invokeMethod(&sync2, "setProp", Qt::QueuedConnection , Q_ARG(int, 89));
+ QTest::qWait(50);
+ while(!thread.wait(10))
+ QTest::qWait(10);
+ QCOMPARE(sync2.m_prop, 89);
+ QCOMPARE(sync1.m_prop, 89);
+}
+
+void tst_QThread::connectThreadFinishedSignalToObjectDeleteLaterSlot()
+{
+ QThread thread;
+ QObject *object = new QObject;
+ QWeakPointer<QObject> p = object;
+ QVERIFY(!p.isNull());
+ connect(&thread, SIGNAL(started()), &thread, SLOT(quit()), Qt::DirectConnection);
+ connect(&thread, SIGNAL(finished()), object, SLOT(deleteLater()));
+ object->moveToThread(&thread);
+ thread.start();
+ QVERIFY(thread.wait(30000));
+ QVERIFY(p.isNull());
+}
+
+class Waiting_Thread : public QThread
+{
+public:
+ enum { WaitTime = 800 };
+ QMutex mutex;
+ QWaitCondition cond1;
+ QWaitCondition cond2;
+
+ void run()
+ {
+ QMutexLocker locker(&mutex);
+ cond1.wait(&mutex);
+ cond2.wait(&mutex, WaitTime);
+ }
+};
+
+void tst_QThread::wait2()
+{
+ QElapsedTimer timer;
+ Waiting_Thread thread;
+ thread.start();
+ timer.start();
+ QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
+ qint64 elapsed = timer.elapsed();
+
+ QVERIFY(elapsed >= Waiting_Thread::WaitTime);
+ //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4);
+
+ timer.start();
+ thread.cond1.wakeOne();
+ QVERIFY(thread.wait(/*Waiting_Thread::WaitTime * 1.4*/));
+ elapsed = timer.elapsed();
+ QVERIFY(elapsed >= Waiting_Thread::WaitTime);
+ //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4);
+}
+
+
+class SlowSlotObject : public QObject {
+ Q_OBJECT
+public:
+ QMutex mutex;
+ QWaitCondition cond;
+public slots:
+ void slowSlot() {
+ QMutexLocker locker(&mutex);
+ cond.wait(&mutex);
+ }
+};
+
+void tst_QThread::wait3_slowDestructor()
+{
+ SlowSlotObject slow;
+ QThread thread;
+ QObject::connect(&thread, SIGNAL(finished()), &slow, SLOT(slowSlot()), Qt::DirectConnection);
+
+ enum { WaitTime = 1800 };
+ QElapsedTimer timer;
+
+ thread.start();
+ thread.quit();
+ //the quit function will cause the thread to finish and enter the slowSlot that is blocking
+
+ timer.start();
+ QVERIFY(!thread.wait(Waiting_Thread::WaitTime));
+ qint64 elapsed = timer.elapsed();
+
+ QVERIFY(elapsed >= Waiting_Thread::WaitTime);
+ //QVERIFY(elapsed < Waiting_Thread::WaitTime * 1.4);
+
+ slow.cond.wakeOne();
+ //now the thread should finish quickly
+ QVERIFY(thread.wait(one_minute));
+}
+
+void tst_QThread::destroyFinishRace()
+{
+ class Thread : public QThread { void run() {} };
+ for (int i = 0; i < 15; i++) {
+ Thread *thr = new Thread;
+ connect(thr, SIGNAL(finished()), thr, SLOT(deleteLater()));
+ QWeakPointer<QThread> weak(static_cast<QThread*>(thr));
+ thr->start();
+ while (weak) {
+ qApp->processEvents();
+ qApp->processEvents();
+ qApp->processEvents();
+ qApp->processEvents();
+ }
+ }
+}
+
+void tst_QThread::startFinishRace()
+{
+ class Thread : public QThread {
+ public:
+ Thread() : i (50) {}
+ void run() {
+ i--;
+ if (!i) disconnect(this, SIGNAL(finished()), 0, 0);
+ }
+ int i;
+ };
+ for (int i = 0; i < 15; i++) {
+ Thread thr;
+ connect(&thr, SIGNAL(finished()), &thr, SLOT(start()));
+ thr.start();
+ while (!thr.isFinished() || thr.i != 0) {
+ qApp->processEvents();
+ qApp->processEvents();
+ qApp->processEvents();
+ qApp->processEvents();
+ }
+ QCOMPARE(thr.i, 0);
+ }
+}
+
+void tst_QThread::startAndQuitCustomEventLoop()
+{
+ struct Thread : QThread {
+ void run() { QEventLoop().exec(); }
+ };
+
+ for (int i = 0; i < 5; i++) {
+ Thread t;
+ t.start();
+ t.quit();
+ t.wait();
+ }
+}
+
+
+QTEST_MAIN(tst_QThread)
+#include "tst_qthread.moc"