qt5base-lts/tests/auto/qthread/tst_qthread.cpp
Qt by Nokia 38be0d1383 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
2011-04-27 12:05:43 +02:00

1250 lines
34 KiB
C++

/****************************************************************************
**
** 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"