From 82e715b2770258fa2c536aeae1f46c7fccdcdecf Mon Sep 17 00:00:00 2001 From: Holger Ihrig Date: Fri, 26 Aug 2011 12:56:14 +0200 Subject: 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 Reviewed-by: Rohan McGovern Reviewed-by: Jason McDonald --- tests/auto/corelib/thread/qthread/.gitignore | 1 + tests/auto/corelib/thread/qthread/qthread.pro | 5 + tests/auto/corelib/thread/qthread/tst_qthread.cpp | 1249 +++++++++++++++++++++ 3 files changed, 1255 insertions(+) create mode 100644 tests/auto/corelib/thread/qthread/.gitignore create mode 100644 tests/auto/corelib/thread/qthread/qthread.pro create mode 100644 tests/auto/corelib/thread/qthread/tst_qthread.cpp (limited to 'tests/auto/corelib/thread/qthread') 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_UNIX +#include +#endif +#if defined(Q_OS_WINCE) +#include +#elif defined(Q_OS_WIN) +#include +#include +#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(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(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 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 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 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 weak(static_cast(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" -- cgit v1.2.3