summaryrefslogtreecommitdiffstats
path: root/tests/auto/qthread/tst_qthread.cpp
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/qthread/tst_qthread.cpp
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/qthread/tst_qthread.cpp')
-rw-r--r--tests/auto/qthread/tst_qthread.cpp1249
1 files changed, 1249 insertions, 0 deletions
diff --git a/tests/auto/qthread/tst_qthread.cpp b/tests/auto/qthread/tst_qthread.cpp
new file mode 100644
index 0000000000..6ad0678745
--- /dev/null
+++ b/tests/auto/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$
+** 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 <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);
+ Q_ASSERT_X(false, "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"