--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016-2019 Ivailo Monev
+**
+** This file is part of the test suite of the Katie Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qatomic.h>
+#include <qcoreapplication.h>
+#include <qdatetime.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+#include <qsemaphore.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QMutex : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QMutex();
+ virtual ~tst_QMutex();
+
+private slots:
+ void tryLock();
+ void lock_unlock_locked_tryLock();
+ void stressTest();
+ void tryLockRace();
+ void qtbug16115_trylock();
+};
+
+static const int iterations = 100;
+
+tst_QMutex::tst_QMutex()
+
+{
+}
+
+tst_QMutex::~tst_QMutex()
+{
+}
+
+QAtomicInt lockCount(0);
+QMutex normalMutex, recursiveMutex(QMutex::Recursive);
+QSemaphore testsTurn;
+QSemaphore threadsTurn;
+
+void tst_QMutex::tryLock()
+{
+ // test non-recursive mutex
+ {
+ class Thread : public QThread
+ {
+ public:
+ void run()
+ {
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QVERIFY(!normalMutex.tryLock());
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QVERIFY(normalMutex.tryLock());
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ QVERIFY(!normalMutex.tryLock());
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ normalMutex.unlock();
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QTime timer;
+ timer.start();
+ QVERIFY(!normalMutex.tryLock(1000));
+ QVERIFY(timer.elapsed() >= 1000);
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ timer.start();
+ QVERIFY(normalMutex.tryLock(1000));
+ QVERIFY(timer.elapsed() <= 1000);
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ timer.start();
+ QVERIFY(!normalMutex.tryLock(1000));
+ QVERIFY(timer.elapsed() >= 1000);
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ normalMutex.unlock();
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QVERIFY(!normalMutex.tryLock(0));
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ timer.start();
+ QVERIFY(normalMutex.tryLock(0));
+ QVERIFY(timer.elapsed() < 1000);
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ QVERIFY(!normalMutex.tryLock(0));
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ normalMutex.unlock();
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ }
+ };
+
+ Thread thread;
+ thread.start();
+
+ // thread can't acquire lock
+ testsTurn.acquire();
+ normalMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ threadsTurn.release();
+
+ // thread can acquire lock
+ testsTurn.acquire();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ normalMutex.unlock();
+ threadsTurn.release();
+
+ // thread can't acquire lock, timeout = 1000
+ testsTurn.acquire();
+ normalMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ threadsTurn.release();
+
+ // thread can acquire lock, timeout = 1000
+ testsTurn.acquire();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ normalMutex.unlock();
+ threadsTurn.release();
+
+ // thread can't acquire lock, timeout = 0
+ testsTurn.acquire();
+ normalMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ threadsTurn.release();
+
+ // thread can acquire lock, timeout = 0
+ testsTurn.acquire();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ normalMutex.unlock();
+ threadsTurn.release();
+
+ // wait for thread to finish
+ testsTurn.acquire();
+ threadsTurn.release();
+ thread.wait();
+ }
+
+ // test recursive mutex
+ {
+ class Thread : public QThread
+ {
+ public:
+ void run()
+ {
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QVERIFY(!recursiveMutex.tryLock());
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QVERIFY(recursiveMutex.tryLock());
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ QVERIFY(recursiveMutex.tryLock());
+ QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+ QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+ recursiveMutex.unlock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ recursiveMutex.unlock();
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QTime timer;
+ timer.start();
+ QVERIFY(!recursiveMutex.tryLock(1000));
+ QVERIFY(timer.elapsed() >= 1000);
+ QVERIFY(!recursiveMutex.tryLock(0));
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ timer.start();
+ QVERIFY(recursiveMutex.tryLock(1000));
+ QVERIFY(timer.elapsed() <= 1000);
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ QVERIFY(recursiveMutex.tryLock(1000));
+ QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+ QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+ recursiveMutex.unlock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ recursiveMutex.unlock();
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ QVERIFY(!recursiveMutex.tryLock(0));
+ QVERIFY(!recursiveMutex.tryLock(0));
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ timer.start();
+ QVERIFY(recursiveMutex.tryLock(0));
+ QVERIFY(timer.elapsed() < 1000);
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ QVERIFY(recursiveMutex.tryLock(0));
+ QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+ QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+ recursiveMutex.unlock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ recursiveMutex.unlock();
+ testsTurn.release();
+
+ threadsTurn.acquire();
+ }
+ };
+
+ Thread thread;
+ thread.start();
+
+ // thread can't acquire lock
+ testsTurn.acquire();
+ recursiveMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ recursiveMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+ threadsTurn.release();
+
+ // thread can acquire lock
+ testsTurn.acquire();
+ QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+ recursiveMutex.unlock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ recursiveMutex.unlock();
+ threadsTurn.release();
+
+ // thread can't acquire lock, timeout = 1000
+ testsTurn.acquire();
+ recursiveMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ recursiveMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+ threadsTurn.release();
+
+ // thread can acquire lock, timeout = 1000
+ testsTurn.acquire();
+ QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+ recursiveMutex.unlock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ recursiveMutex.unlock();
+ threadsTurn.release();
+
+ // thread can't acquire lock, timeout = 0
+ testsTurn.acquire();
+ recursiveMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(0, 1));
+ recursiveMutex.lock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 2));
+ threadsTurn.release();
+
+ // thread can acquire lock, timeout = 0
+ testsTurn.acquire();
+ QVERIFY(lockCount.testAndSetRelaxed(2, 1));
+ recursiveMutex.unlock();
+ QVERIFY(lockCount.testAndSetRelaxed(1, 0));
+ recursiveMutex.unlock();
+ threadsTurn.release();
+
+ // stop thread
+ testsTurn.acquire();
+ threadsTurn.release();
+ thread.wait();
+ }
+}
+
+class mutex_Thread : public QThread
+{
+public:
+ QMutex mutex;
+ QWaitCondition cond;
+
+ QMutex &test_mutex;
+
+ inline mutex_Thread(QMutex &m) : test_mutex(m) { }
+
+ void run()
+ {
+ test_mutex.lock();
+
+ mutex.lock();
+ for (int i = 0; i < iterations; ++i) {
+ cond.wakeOne();
+ cond.wait(&mutex);
+ }
+ mutex.unlock();
+
+ test_mutex.unlock();
+ }
+};
+
+class rmutex_Thread : public QThread
+{
+public:
+ QMutex mutex;
+ QWaitCondition cond;
+
+ QMutex &test_mutex;
+
+ inline rmutex_Thread(QMutex &m) : test_mutex(m) { }
+
+ void run()
+ {
+ test_mutex.lock();
+ test_mutex.lock();
+ test_mutex.lock();
+ test_mutex.lock();
+
+ mutex.lock();
+ for (int i = 0; i < iterations; ++i) {
+ cond.wakeOne();
+ cond.wait(&mutex);
+ }
+ mutex.unlock();
+
+ test_mutex.unlock();
+ test_mutex.unlock();
+ test_mutex.unlock();
+ test_mutex.unlock();
+ }
+};
+
+#ifdef QT3_SUPPORT
+#define VERIFY_LOCKED(x) QVERIFY((x).locked())
+#define VERIFY_NLOCKED(x) QVERIFY(!(x).locked())
+#else
+#define VERIFY_LOCKED(x)
+#define VERIFY_NLOCKED(x)
+#endif // QT3_SUPPORT
+
+void tst_QMutex::lock_unlock_locked_tryLock()
+{
+ // normal mutex
+ QMutex mutex;
+ mutex_Thread thread(mutex);
+
+ QMutex rmutex(QMutex::Recursive);
+ rmutex_Thread rthread(rmutex);
+
+ for (int i = 0; i < iterations; ++i) {
+ // normal mutex
+ VERIFY_NLOCKED(mutex);
+ QVERIFY(mutex.tryLock());
+ mutex.unlock();
+
+ thread.mutex.lock();
+ thread.start();
+
+ for (int j = 0; j < iterations; ++j) {
+ QVERIFY(thread.cond.wait(&thread.mutex, 10000));
+ VERIFY_LOCKED(mutex);
+ QVERIFY(!mutex.tryLock());
+
+ thread.cond.wakeOne();
+ }
+
+ thread.mutex.unlock();
+
+ QVERIFY(thread.wait(10000));
+ VERIFY_NLOCKED(mutex);
+ QVERIFY(mutex.tryLock());
+
+ mutex.unlock();
+
+ // recursive mutex
+ VERIFY_NLOCKED(rmutex);
+ QVERIFY(rmutex.tryLock());
+ QVERIFY(rmutex.tryLock());
+ QVERIFY(rmutex.tryLock());
+ QVERIFY(rmutex.tryLock());
+
+ rmutex.unlock();
+ rmutex.unlock();
+ rmutex.unlock();
+ rmutex.unlock();
+
+ rthread.mutex.lock();
+ rthread.start();
+
+ for (int k = 0; k < iterations; ++k) {
+ QVERIFY(rthread.cond.wait(&rthread.mutex, 10000));
+ VERIFY_LOCKED(rmutex);
+ QVERIFY(!rmutex.tryLock());
+
+ rthread.cond.wakeOne();
+ }
+
+ rthread.mutex.unlock();
+
+ QVERIFY(rthread.wait(10000));
+ VERIFY_NLOCKED(rmutex);
+ QVERIFY(rmutex.tryLock());
+ QVERIFY(rmutex.tryLock());
+ QVERIFY(rmutex.tryLock());
+ QVERIFY(rmutex.tryLock());
+
+ rmutex.unlock();
+ rmutex.unlock();
+ rmutex.unlock();
+ rmutex.unlock();
+ }
+}
+
+enum { one_minute = 60 * 1000, threadCount = 10 };
+
+class StressTestThread : public QThread
+{
+ QTime t;
+public:
+ static QAtomicInt lockCount;
+ static QAtomicInt sentinel;
+ static QMutex mutex;
+ static int errorCount;
+ void start()
+ {
+ t.start();
+ QThread::start();
+ }
+ void run()
+ {
+ while (t.elapsed() < one_minute) {
+ mutex.lock();
+ if (sentinel.ref()) ++errorCount;
+ if (!sentinel.deref()) ++errorCount;
+ lockCount.ref();
+ mutex.unlock();
+ if (mutex.tryLock()) {
+ if (sentinel.ref()) ++errorCount;
+ if (!sentinel.deref()) ++errorCount;
+ lockCount.ref();
+ mutex.unlock();
+ }
+ }
+ }
+};
+QMutex StressTestThread::mutex;
+QAtomicInt StressTestThread::lockCount = QAtomicInt(0);
+QAtomicInt StressTestThread::sentinel = QAtomicInt(-1);
+int StressTestThread::errorCount = 0;
+
+void tst_QMutex::stressTest()
+{
+ StressTestThread threads[threadCount];
+ for (int i = 0; i < threadCount; ++i)
+ threads[i].start();
+ QVERIFY(threads[0].wait(one_minute + 10000));
+ for (int i = 1; i < threadCount; ++i)
+ QVERIFY(threads[i].wait(10000));
+ QCOMPARE(StressTestThread::errorCount, 0);
+ qDebug("locked %d times", int(StressTestThread::lockCount));
+}
+
+class TryLockRaceThread : public QThread
+{
+public:
+ static QMutex mutex;
+
+ void run()
+ {
+ QTime t;
+ t.start();
+ do {
+ if (mutex.tryLock())
+ mutex.unlock();
+ } while (t.elapsed() < 20000);
+ }
+};
+QMutex TryLockRaceThread::mutex;
+
+void tst_QMutex::tryLockRace()
+{
+ // mutex not in use, should be able to lock it
+ QVERIFY(TryLockRaceThread::mutex.tryLock());
+ TryLockRaceThread::mutex.unlock();
+
+ // try to break tryLock
+ TryLockRaceThread thread[threadCount];
+ for (int i = 0; i < threadCount; ++i)
+ thread[i].start();
+ for (int i = 0; i < threadCount; ++i)
+ QVERIFY(thread[i].wait());
+
+ // mutex not in use, should be able to lock it
+ QVERIFY(TryLockRaceThread::mutex.tryLock());
+ TryLockRaceThread::mutex.unlock();
+}
+
+// Variable that will be protected by the mutex. Volatile so that the
+// the optimiser doesn't mess with it based on the increment-then-decrement
+// usage pattern.
+static volatile int qtbug16115_trylock_counter;
+// Counter for how many times the protected variable has an incorrect value.
+static int qtbug16115_failure_count = 0;
+
+void tst_QMutex::qtbug16115_trylock()
+{
+ //Used to deadlock on unix
+ struct TrylockThread : QThread {
+ TrylockThread(QMutex &mut) : mut(mut) {}
+ QMutex &mut;
+ void run() {
+ for (int i = 0; i < 1000000; ++i) {
+ if (mut.tryLock(0)) {
+ if ((++qtbug16115_trylock_counter) != 1)
+ ++qtbug16115_failure_count;
+ if ((--qtbug16115_trylock_counter) != 0)
+ ++qtbug16115_failure_count;
+ mut.unlock();
+ }
+ }
+ }
+ };
+ QMutex mut;
+ TrylockThread t1(mut);
+ TrylockThread t2(mut);
+ TrylockThread t3(mut);
+ t1.start();
+ t2.start();
+ t3.start();
+
+ for (int i = 0; i < 1000000; ++i) {
+ mut.lock();
+ if ((++qtbug16115_trylock_counter) != 1)
+ ++qtbug16115_failure_count;
+ if ((--qtbug16115_trylock_counter) != 0)
+ ++qtbug16115_failure_count;
+ mut.unlock();
+ }
+ t1.wait();
+ t2.wait();
+ t3.wait();
+ QCOMPARE(qtbug16115_failure_count, 0);
+}
+
+QTEST_MAIN(tst_QMutex)
+
+#include "moc_tst_qmutex.cpp"
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016-2019 Ivailo Monev
+**
+** This file is part of the test suite of the Katie Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company 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.
+**
+** $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>
+#include <qsemaphore.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 adoptThreadExitWithActiveTimer();
+ 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 isRunningInFinished();
+
+ void stressTest();
+#ifdef Q_OS_SYMBIAN
+ void threadNameTest();
+#endif
+};
+
+enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute };
+
+class SignalRecorder : public QObject
+{
+ Q_OBJECT
+public:
+ QAtomicInt activationCount;
+
+ inline SignalRecorder()
+ { activationCount = 0; }
+
+ bool wasActivated()
+ { return activationCount > 0; }
+
+public slots:
+ void slot();
+};
+
+void SignalRecorder::slot()
+{ activationCount.ref(); }
+
+class Current_Thread : public QThread
+{
+public:
+ Qt::HANDLE id;
+ QThread *thread;
+
+ void run()
+ {
+ id = QThread::currentThreadId();
+ thread = QThread::currentThread();
+ }
+};
+
+class Simple_Thread : public QThread
+{
+public:
+ QMutex mutex;
+ QWaitCondition cond;
+
+ void run()
+ {
+ QMutexLocker locker(&mutex);
+ cond.wakeOne();
+ }
+};
+
+class Exit_Object : public QObject
+{
+ Q_OBJECT
+public:
+ QThread *thread;
+ int code;
+public slots:
+ void slot()
+ { thread->exit(code); }
+};
+
+class Exit_Thread : public Simple_Thread
+{
+public:
+ Exit_Object *object;
+ int code;
+ int result;
+
+ void run()
+ {
+ Simple_Thread::run();
+ if (object) {
+ object->thread = this;
+ object->code = code;
+ QTimer::singleShot(100, object, SLOT(slot()));
+ }
+ result = exec();
+ }
+};
+
+class Terminate_Thread : public Simple_Thread
+{
+public:
+ void run()
+ {
+ setTerminationEnabled(false);
+ {
+ QMutexLocker locker(&mutex);
+ cond.wakeOne();
+ cond.wait(&mutex, five_minutes);
+ }
+ setTerminationEnabled(true);
+ qFatal("tst_QThread: test case hung");
+ }
+};
+
+class Quit_Object : public QObject
+{
+ Q_OBJECT
+public:
+ QThread *thread;
+public slots:
+ void slot()
+ { thread->quit(); }
+};
+
+class Quit_Thread : public Simple_Thread
+{
+public:
+ Quit_Object *object;
+ int result;
+
+ void run()
+ {
+ Simple_Thread::run();
+ if (object) {
+ object->thread = this;
+ QTimer::singleShot(100, object, SLOT(slot()));
+ }
+ result = exec();
+ }
+};
+
+class Sleep_Thread : public Simple_Thread
+{
+public:
+ enum SleepType { Second, Millisecond, Microsecond };
+
+ SleepType sleepType;
+ int interval;
+
+ int elapsed; // result, in *MILLISECONDS*
+
+ void run()
+ {
+ QMutexLocker locker(&mutex);
+
+ elapsed = 0;
+ QTime time;
+ time.start();
+ switch (sleepType) {
+ case Second:
+ sleep(interval);
+ break;
+ case Millisecond:
+ msleep(interval);
+ break;
+ case Microsecond:
+ usleep(interval);
+ break;
+ }
+ elapsed = time.elapsed();
+
+ cond.wakeOne();
+ }
+};
+
+tst_QThread::tst_QThread()
+
+{
+}
+
+tst_QThread::~tst_QThread()
+{
+
+}
+
+void tst_QThread::currentThreadId()
+{
+ Current_Thread thread;
+ thread.id = 0;
+ thread.thread = 0;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.id != 0);
+ QVERIFY(thread.id != QThread::currentThreadId());
+}
+
+void tst_QThread::currentThread()
+{
+ QVERIFY(QThread::currentThread() != 0);
+ QCOMPARE(QThread::currentThread(), thread());
+
+ Current_Thread thread;
+ thread.id = 0;
+ thread.thread = 0;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QCOMPARE(thread.thread, (QThread *)&thread);
+}
+
+void tst_QThread::idealThreadCount()
+{
+ QVERIFY(QThread::idealThreadCount() > 0);
+ qDebug() << "Available cpu cores:" << QThread::idealThreadCount();
+}
+
+void tst_QThread::isFinished()
+{
+ Simple_Thread thread;
+ QVERIFY(!thread.isFinished());
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(!thread.isFinished());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.isFinished());
+}
+
+void tst_QThread::isRunning()
+{
+ Simple_Thread thread;
+ QVERIFY(!thread.isRunning());
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(thread.isRunning());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(!thread.isRunning());
+}
+
+void tst_QThread::setPriority()
+{
+ Simple_Thread thread;
+
+ // cannot change the priority, since the thread is not running
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::IdlePriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::LowestPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::LowPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::NormalPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::HighPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::HighestPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::TimeCriticalPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+
+ // change the priority of a running thread
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ thread.setPriority(QThread::IdlePriority);
+ QCOMPARE(thread.priority(), QThread::IdlePriority);
+ thread.setPriority(QThread::LowestPriority);
+ QCOMPARE(thread.priority(), QThread::LowestPriority);
+ thread.setPriority(QThread::LowPriority);
+ QCOMPARE(thread.priority(), QThread::LowPriority);
+ thread.setPriority(QThread::NormalPriority);
+ QCOMPARE(thread.priority(), QThread::NormalPriority);
+ thread.setPriority(QThread::HighPriority);
+ QCOMPARE(thread.priority(), QThread::HighPriority);
+ thread.setPriority(QThread::HighestPriority);
+ QCOMPARE(thread.priority(), QThread::HighestPriority);
+ thread.setPriority(QThread::TimeCriticalPriority);
+ QCOMPARE(thread.priority(), QThread::TimeCriticalPriority);
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::IdlePriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::LowestPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::LowPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::NormalPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::HighPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::HighestPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+ QTest::ignoreMessage(QtWarningMsg, "QThread::setPriority: Cannot set priority, thread is not running");
+ thread.setPriority(QThread::TimeCriticalPriority);
+ QCOMPARE(thread.priority(), QThread::InheritPriority);
+}
+
+void tst_QThread::priority()
+{ DEPENDS_ON("setPriority"); }
+
+void tst_QThread::setStackSize()
+{
+ Simple_Thread thread;
+ QCOMPARE(thread.stackSize(), 0u);
+ thread.setStackSize(8192u);
+ QCOMPARE(thread.stackSize(), 8192u);
+ thread.setStackSize(0u);
+ QCOMPARE(thread.stackSize(), 0u);
+}
+
+void tst_QThread::stackSize()
+{
+ DEPENDS_ON("setStackSize");
+}
+
+void tst_QThread::exit()
+{
+ Exit_Thread thread;
+ thread.object = new Exit_Object;
+ thread.object->moveToThread(&thread);
+ thread.code = 42;
+ thread.result = 0;
+ QVERIFY(!thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(thread.isRunning());
+ QVERIFY(!thread.isFinished());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QCOMPARE(thread.result, thread.code);
+ delete thread.object;
+
+ Exit_Thread thread2;
+ thread2.object = 0;
+ thread2.code = 53;
+ thread2.result = 0;
+ QMutexLocker locker2(&thread2.mutex);
+ thread2.start();
+ thread2.exit(thread2.code);
+ thread2.cond.wait(locker2.mutex());
+ QVERIFY(thread2.wait(five_minutes));
+ QCOMPARE(thread2.result, thread2.code);
+}
+
+void tst_QThread::start()
+{
+ QThread::Priority priorities[] = {
+ QThread::IdlePriority,
+ QThread::LowestPriority,
+ QThread::LowPriority,
+ QThread::NormalPriority,
+ QThread::HighPriority,
+ QThread::HighestPriority,
+ QThread::TimeCriticalPriority,
+ QThread::InheritPriority
+ };
+ const int prio_count = sizeof(priorities) / sizeof(QThread::Priority);
+
+ for (int i = 0; i < prio_count; ++i) {
+ Simple_Thread thread;
+ QVERIFY(!thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QMutexLocker locker(&thread.mutex);
+ thread.start(priorities[i]);
+ QVERIFY(thread.isRunning());
+ QVERIFY(!thread.isFinished());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ }
+}
+
+void tst_QThread::terminate()
+{
+ Terminate_Thread thread;
+ {
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(thread.cond.wait(locker.mutex(), five_minutes));
+ thread.terminate();
+ thread.cond.wakeOne();
+ }
+ QVERIFY(thread.wait(five_minutes));
+}
+
+void tst_QThread::quit()
+{
+ Quit_Thread thread;
+ thread.object = new Quit_Object;
+ thread.object->moveToThread(&thread);
+ thread.result = -1;
+ QVERIFY(!thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ QVERIFY(thread.isRunning());
+ QVERIFY(!thread.isFinished());
+ thread.cond.wait(locker.mutex());
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.isFinished());
+ QVERIFY(!thread.isRunning());
+ QCOMPARE(thread.result, 0);
+ delete thread.object;
+
+ Quit_Thread thread2;
+ thread2.object = 0;
+ thread2.result = -1;
+ QMutexLocker locker2(&thread2.mutex);
+ thread2.start();
+ thread2.quit();
+ thread2.cond.wait(locker2.mutex());
+ QVERIFY(thread2.wait(five_minutes));
+ QCOMPARE(thread2.result, 0);
+}
+
+void tst_QThread::wait()
+{
+ DEPENDS_ON("isRunning");
+ DEPENDS_ON("isFinished");
+}
+
+void tst_QThread::started()
+{
+ SignalRecorder recorder;
+ Simple_Thread thread;
+ connect(&thread, SIGNAL(started()), &recorder, SLOT(slot()), Qt::DirectConnection);
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::finished()
+{
+ SignalRecorder recorder;
+ Simple_Thread thread;
+ connect(&thread, SIGNAL(finished()), &recorder, SLOT(slot()), Qt::DirectConnection);
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::terminated()
+{
+ SignalRecorder recorder;
+ Terminate_Thread thread;
+ connect(&thread, SIGNAL(terminated()), &recorder, SLOT(slot()), Qt::DirectConnection);
+ {
+ QMutexLocker locker(&thread.mutex);
+ thread.start();
+ thread.cond.wait(locker.mutex());
+ thread.terminate();
+ thread.cond.wakeOne();
+ }
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(recorder.wasActivated());
+}
+
+void tst_QThread::run()
+{ DEPENDS_ON("wait()"); }
+
+void tst_QThread::exec()
+{
+ DEPENDS_ON("exit()");
+ DEPENDS_ON("quit()");
+
+ class MultipleExecThread : public QThread
+ {
+ public:
+ int res1, res2;
+
+ MultipleExecThread() : res1(-2), res2(-2) { }
+
+ void run()
+ {
+ {
+ Exit_Object o;
+ o.thread = this;
+ o.code = 1;
+ QTimer::singleShot(100, &o, SLOT(slot()));
+ res1 = exec();
+ }
+ {
+ Exit_Object o;
+ o.thread = this;
+ o.code = 2;
+ QTimer::singleShot(100, &o, SLOT(slot()));
+ res2 = exec();
+ }
+ }
+ };
+
+ MultipleExecThread thread;
+ thread.start();
+ QVERIFY(thread.wait());
+
+ QCOMPARE(thread.res1, 1);
+ QCOMPARE(thread.res2, 2);
+}
+
+void tst_QThread::setTerminationEnabled()
+{ DEPENDS_ON("terminate"); }
+
+void tst_QThread::sleep()
+{
+ Sleep_Thread thread;
+ thread.sleepType = Sleep_Thread::Second;
+ thread.interval = 2;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+ QVERIFY(thread.elapsed >= 2000);
+}
+
+void tst_QThread::msleep()
+{
+ Sleep_Thread thread;
+ thread.sleepType = Sleep_Thread::Millisecond;
+ thread.interval = 120;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+#if defined (Q_OS_WIN)
+ // Since the resolution of QTime is so coarse...
+ QVERIFY(thread.elapsed >= 100);
+#else
+ QVERIFY(thread.elapsed >= 120);
+#endif
+}
+
+void tst_QThread::usleep()
+{
+ Sleep_Thread thread;
+ thread.sleepType = Sleep_Thread::Microsecond;
+ thread.interval = 120000;
+ thread.start();
+ QVERIFY(thread.wait(five_minutes));
+#if defined (Q_OS_WIN)
+ // Since the resolution of QTime is so coarse...
+ QVERIFY(thread.elapsed >= 100);
+#else
+ QVERIFY(thread.elapsed >= 120);
+#endif
+}
+
+typedef void (*FunctionPointer)(void *);
+void noop(void*) { }
+
+#ifdef Q_OS_SYMBIAN
+typedef RThread ThreadHandle;
+#elif defined Q_OS_UNIX
+ typedef pthread_t ThreadHandle;
+#elif defined Q_OS_WIN
+ typedef HANDLE ThreadHandle;
+#endif
+
+#ifdef Q_OS_WIN
+#define WIN_FIX_STDCALL __stdcall
+#else
+#define WIN_FIX_STDCALL
+#endif
+
+class NativeThreadWrapper
+{
+public:
+ NativeThreadWrapper() : qthread(0), waitForStop(false) {}
+ void start(FunctionPointer functionPointer = noop, void *data = 0);
+ void startAndWait(FunctionPointer functionPointer = noop, void *data = 0);
+ void join();
+ void setWaitForStop() { waitForStop = true; }
+ void stop();
+
+ ThreadHandle nativeThreadHandle;
+ QThread *qthread;
+ QWaitCondition startCondition;
+ QMutex mutex;
+ bool waitForStop;
+ QWaitCondition stopCondition;
+protected:
+ static void *runUnix(void *data);
+ static unsigned WIN_FIX_STDCALL runWin(void *data);
+ static int runSymbian(void *data);
+
+ FunctionPointer functionPointer;
+ void *data;
+};
+
+void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data)
+{
+ this->functionPointer = functionPointer;
+ this->data = data;
+#ifdef Q_OS_SYMBIAN
+ qt_symbian_throwIfError(nativeThreadHandle.Create(KNullDesC(), NativeThreadWrapper::runSymbian, 1024, &User::Allocator(), this));
+ nativeThreadHandle.Resume();
+#elif defined Q_OS_UNIX
+ const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this);
+ Q_UNUSED(state);
+#elif defined(Q_OS_WINCE)
+ nativeThreadHandle = CreateThread(NULL, 0 , (LPTHREAD_START_ROUTINE)NativeThreadWrapper::runWin , this, 0, NULL);
+#elif defined Q_OS_WIN
+ unsigned thrdid = 0;
+ nativeThreadHandle = (Qt::HANDLE) _beginthreadex(NULL, 0, NativeThreadWrapper::runWin, this, 0, &thrdid);
+#endif
+}
+
+void NativeThreadWrapper::startAndWait(FunctionPointer functionPointer, void *data)
+{
+ QMutexLocker locker(&mutex);
+ start(functionPointer, data);
+ startCondition.wait(locker.mutex());
+}
+
+void NativeThreadWrapper::join()
+{
+#ifdef Q_OS_SYMBIAN
+ TRequestStatus stat;
+ nativeThreadHandle.Logon(stat);
+ User::WaitForRequest(stat);
+ nativeThreadHandle.Close();
+#elif defined Q_OS_UNIX
+ pthread_join(nativeThreadHandle, 0);
+#elif defined Q_OS_WIN
+ WaitForSingleObject(nativeThreadHandle, INFINITE);
+ CloseHandle(nativeThreadHandle);
+#endif
+}
+
+void *NativeThreadWrapper::runUnix(void *that)
+{
+ NativeThreadWrapper *nativeThreadWrapper = reinterpret_cast<NativeThreadWrapper*>(that);
+
+ // Adopt thread, create QThread object.
+ nativeThreadWrapper->qthread = QThread::currentThread();
+
+ // Release main thread.
+ {
+ QMutexLocker lock(&nativeThreadWrapper->mutex);
+ nativeThreadWrapper->startCondition.wakeOne();
+ }
+
+ // Run function.
+ nativeThreadWrapper->functionPointer(nativeThreadWrapper->data);
+
+ // Wait for stop.
+ {
+ QMutexLocker lock(&nativeThreadWrapper->mutex);
+ if (nativeThreadWrapper->waitForStop)
+ nativeThreadWrapper->stopCondition.wait(lock.mutex());
+ }
+
+ return 0;
+}
+
+unsigned WIN_FIX_STDCALL NativeThreadWrapper::runWin(void *data)
+{
+ runUnix(data);
+ return 0;
+}
+
+int NativeThreadWrapper::runSymbian(void *data)
+{
+ runUnix(data);
+ return 0;
+}
+
+void NativeThreadWrapper::stop()
+{
+ QMutexLocker lock(&mutex);
+ waitForStop = false;
+ stopCondition.wakeOne();
+}
+
+bool threadAdoptedOk = false;
+QThread *mainThread;
+void testNativeThreadAdoption(void *)
+{
+ threadAdoptedOk = (QThread::currentThreadId() != 0
+ && QThread::currentThread() != 0
+ && QThread::currentThread() != mainThread);
+}
+void tst_QThread::nativeThreadAdoption()
+{
+ threadAdoptedOk = false;
+ mainThread = QThread::currentThread();
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+ nativeThread.startAndWait(testNativeThreadAdoption);
+ QVERIFY(nativeThread.qthread);
+
+ nativeThread.stop();
+ nativeThread.join();
+
+ QVERIFY(threadAdoptedOk);
+}
+
+void adoptedThreadAffinityFunction(void *arg)
+{
+ QThread **affinity = reinterpret_cast<QThread **>(arg);
+ QThread *current = QThread::currentThread();
+ affinity[0] = current;
+ affinity[1] = current->thread();
+}
+
+void tst_QThread::adoptedThreadAffinity()
+{
+ QThread *affinity[2] = { 0, 0 };
+
+ NativeThreadWrapper thread;
+ thread.startAndWait(adoptedThreadAffinityFunction, affinity);
+ thread.join();
+
+ // adopted thread should have affinity to itself
+ QCOMPARE(affinity[0], affinity[1]);
+}
+
+void tst_QThread::adoptedThreadSetPriority()
+{
+
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+ nativeThread.startAndWait();
+
+ // change the priority of a running thread
+ QCOMPARE(nativeThread.qthread->priority(), QThread::InheritPriority);
+ nativeThread.qthread->setPriority(QThread::IdlePriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::IdlePriority);
+ nativeThread.qthread->setPriority(QThread::LowestPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::LowestPriority);
+ nativeThread.qthread->setPriority(QThread::LowPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::LowPriority);
+ nativeThread.qthread->setPriority(QThread::NormalPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::NormalPriority);
+ nativeThread.qthread->setPriority(QThread::HighPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::HighPriority);
+ nativeThread.qthread->setPriority(QThread::HighestPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::HighestPriority);
+ nativeThread.qthread->setPriority(QThread::TimeCriticalPriority);
+ QCOMPARE(nativeThread.qthread->priority(), QThread::TimeCriticalPriority);
+
+ nativeThread.stop();
+ nativeThread.join();
+}
+
+void tst_QThread::adoptedThreadExit()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+
+ nativeThread.startAndWait();
+ QVERIFY(nativeThread.qthread);
+ QVERIFY(nativeThread.qthread->isRunning());
+ QVERIFY(!nativeThread.qthread->isFinished());
+
+ nativeThread.stop();
+ nativeThread.join();
+}
+
+void adoptedThreadExecFunction(void *)
+{
+ QThread * const adoptedThread = QThread::currentThread();
+ QEventLoop eventLoop(adoptedThread);
+
+ const int code = 1;
+ Exit_Object o;
+ o.thread = adoptedThread;
+ o.code = code;
+ QTimer::singleShot(100, &o, SLOT(slot()));
+
+ const int result = eventLoop.exec();
+ QCOMPARE(result, code);
+}
+
+void tst_QThread::adoptedThreadExec()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.start(adoptedThreadExecFunction);
+ nativeThread.join();
+}
+
+/*
+ Test that you get the finished signal when an adopted thread exits.
+*/
+void tst_QThread::adoptedThreadFinished()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+ nativeThread.startAndWait();
+
+ QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ nativeThread.stop();
+ nativeThread.join();
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
+void tst_QThread::adoptedThreadExecFinished()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.setWaitForStop();
+ nativeThread.startAndWait(adoptedThreadExecFunction);
+
+ QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ nativeThread.stop();
+ nativeThread.join();
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+}
+
+void tst_QThread::adoptMultipleThreads()
+{
+#if defined(Q_OS_WIN)
+ // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already.
+# if defined(Q_OS_WINCE)
+ const int numThreads = 20;
+# else
+ // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher()
+ const int numThreads = 200;
+# endif
+#else
+ const int numThreads = 5;
+#endif
+ QVector<NativeThreadWrapper*> nativeThreads;
+
+ SignalRecorder recorder;
+
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.append(new NativeThreadWrapper());
+ nativeThreads.at(i)->setWaitForStop();
+ nativeThreads.at(i)->startAndWait();
+ QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot()));
+ }
+
+ QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.at(i)->stop();
+ nativeThreads.at(i)->join();
+ delete nativeThreads.at(i);
+ }
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QCOMPARE(int(recorder.activationCount), numThreads);
+}
+
+void tst_QThread::adoptMultipleThreadsOverlap()
+{
+#if defined(Q_OS_WIN)
+ // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already.
+# if defined(Q_OS_WINCE)
+ const int numThreads = 20;
+# else
+ // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher()
+ const int numThreads = 200;
+# endif
+#elif defined(Q_OS_SYMBIAN)
+ // stress the monitoring thread's add function
+ const int numThreads = 100;
+#else
+ const int numThreads = 5;
+#endif
+ QVector<NativeThreadWrapper*> nativeThreads;
+
+ SignalRecorder recorder;
+
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.append(new NativeThreadWrapper());
+ nativeThreads.at(i)->setWaitForStop();
+ nativeThreads.at(i)->mutex.lock();
+ nativeThreads.at(i)->start();
+ }
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.at(i)->startCondition.wait(&nativeThreads.at(i)->mutex);
+ QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot()));
+ nativeThreads.at(i)->mutex.unlock();
+ }
+
+ QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
+
+ for (int i = 0; i < numThreads; ++i) {
+ nativeThreads.at(i)->stop();
+ nativeThreads.at(i)->join();
+ delete nativeThreads.at(i);
+ }
+
+ QTestEventLoop::instance().enterLoop(5);
+ QVERIFY(!QTestEventLoop::instance().timeout());
+ QCOMPARE(int(recorder.activationCount), numThreads);
+}
+
+void adoptedThreadActiveTimerFunction(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()));
+
+ // create a timer that will still be active when the thread finishes
+ QTimer::singleShot(3000, &o, SLOT(slot()));
+
+ const int result = eventLoop.exec();
+ QCOMPARE(result, code);
+}
+
+void tst_QThread::adoptThreadExitWithActiveTimer()
+{
+ NativeThreadWrapper nativeThread;
+ nativeThread.start(adoptedThreadActiveTimerFunction);
+ nativeThread.join();
+}
+
+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();
+ }
+}
+
+#ifdef Q_OS_SYMBIAN
+#include <QRegExp>
+
+namespace testNamespace {
+class TestThread : public QThread
+{
+ Q_OBJECT
+public:
+ TestThread();
+ void run();
+public:
+ bool runCalled;
+ QString threadName;
+};
+
+TestThread::TestThread() : runCalled(false)
+{
+}
+
+void TestThread::run()
+{
+ runCalled = true;
+ RThread t;
+ TName name = t.Name();
+ threadName = QString((QChar*)name.Ptr(), name.Length());
+}
+}
+
+void tst_QThread::threadNameTest()
+{
+// On Symbian thread name consist of objectName, className and variable part.
+// RThread::Create sets limitations on what are allowed characters in thread name.
+// Allowed characters include chars in 0x20 - 0x7e range but not '*','?',':'
+// If thread thread name contains not allowed characters RThread::Create fails.
+// In addition, max thread name length is 80 chars on Symbian.
+
+ // Reqular expression used in QThread::start for removing not allowed characters
+ const QRegExp notAllowedChars(QLatin1String("[^\\x20-\\x7e]|\\*|\\?|\\:"));
+
+ // objectName contains all allowed characters
+ {
+ testNamespace::TestThread thread;
+ QString name;
+ for (int i = 0x20; i < 0x7f; i++) {
+ if (i != '*' && i != '?' && i != ':') {
+ name.append(QLatin1Char(i));
+ }
+ }
+
+ thread.setObjectName(name);
+ thread.start();
+ thread.wait();
+ QCOMPARE(thread.runCalled, true);
+
+ QString expectedResult = name;
+ QString result = name.replace(notAllowedChars, QLatin1String("_"));
+ QCOMPARE(result, expectedResult);
+ // objectName part can be max 72 chars in thread name
+ QCOMPARE(thread.threadName.left(72), expectedResult.left(72));
+ }
+
+ // objectName contains all characters from range including characters deemed
+ // not valid by RThread::Create (*?:)
+ {
+ testNamespace::TestThread thread;
+ QString name;
+ for (int i = 0x20; i < 0x7f; i++) {
+ name.append(QLatin1Char(i));
+ }
+
+ thread.setObjectName(name);
+ thread.start();
+ thread.wait();
+ QCOMPARE(thread.runCalled, true);
+
+ QString expectedResult = name;
+ expectedResult = expectedResult.replace(QLatin1Char('*'), QLatin1Char('_'));
+ expectedResult = expectedResult.replace(QLatin1Char('?'), QLatin1Char('_'));
+ expectedResult = expectedResult.replace(QLatin1Char(':'), QLatin1Char('_'));
+ QString result = name.replace(notAllowedChars, QLatin1String("_"));
+ QCOMPARE(result, expectedResult);
+
+ // objectName part can be max 72 chars in thread name
+ QCOMPARE(thread.threadName.left(72), expectedResult.left(72));
+ }
+
+ // objectName contains only invalid characters
+ {
+ testNamespace::TestThread thread;
+ QString name;
+ for (int i = 0; i < 0x20; i++) {
+ name.append(QLatin1Char(i));
+ }
+ for (int i = 0x7f; i < 0xff; i++) {
+ name.append(QLatin1Char(i));
+ }
+
+ thread.setObjectName(name);
+ thread.start();
+ thread.wait();
+ QCOMPARE(thread.runCalled, true);
+
+ QString expectedResult;
+ expectedResult.fill(QLatin1Char('_'), name.size());
+ QString result = name.replace(notAllowedChars, QLatin1String("_"));
+ QCOMPARE(result, expectedResult);
+
+ // objectName part can be max 72 chars in thread name
+ QCOMPARE(thread.threadName.left(72), expectedResult.left(72));
+ }
+
+ // objectName longer than max thread name length (80 chars)
+ {
+ testNamespace::TestThread thread;
+ QString name;
+ for (int i = 0; i < 0xff; i++) {
+ name.append(QLatin1Char(i));
+ }
+
+ thread.setObjectName(name);
+ thread.start();
+ thread.wait();
+ QCOMPARE(thread.runCalled, true);
+ }
+
+ // className contains not allowed characters (':')
+ {
+ testNamespace::TestThread thread;
+ thread.start();
+ thread.wait();
+ QCOMPARE(thread.runCalled, true);
+ QString className(QLatin1String(thread.metaObject()->className()));
+ QCOMPARE(className, QLatin1String("testNamespace::TestThread"));
+
+ QString expectedResult = className;
+ expectedResult = className.replace(QLatin1Char(':'), QLatin1Char('_'));
+ QString result = className.replace(notAllowedChars, QLatin1String("_"));
+ QCOMPARE(result, expectedResult);
+
+ QVERIFY(thread.threadName.contains(expectedResult));
+ }
+}
+#endif // Q_OS_SYMBIAN
+
+class FinishedTestObject : public QObject {
+ Q_OBJECT
+public:
+ FinishedTestObject() : ok(false) {}
+ bool ok;
+public slots:
+ void slotFinished() {
+ QThread *t = qobject_cast<QThread *>(sender());
+ ok = t && t->isFinished() && !t->isRunning();
+ }
+};
+
+void tst_QThread::isRunningInFinished()
+{
+ for (int i = 0; i < 15; i++) {
+ QThread thread;
+ thread.start();
+ FinishedTestObject localObject;
+ FinishedTestObject inThreadObject;
+ localObject.setObjectName("...");
+ inThreadObject.moveToThread(&thread);
+ connect(&thread, SIGNAL(finished()), &localObject, SLOT(slotFinished()));
+ connect(&thread, SIGNAL(finished()), &inThreadObject, SLOT(slotFinished()));
+ QEventLoop loop;
+ connect(&thread, SIGNAL(finished()), &loop, SLOT(quit()));
+ QMetaObject::invokeMethod(&thread, "quit", Qt::QueuedConnection);
+ loop.exec();
+ QVERIFY(!thread.isRunning());
+ QVERIFY(thread.isFinished());
+ QVERIFY(localObject.ok);
+ QVERIFY(inThreadObject.ok);
+ }
+}
+
+QTEST_MAIN(tst_QThread)
+#include "moc_tst_qthread.cpp"
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2016-2019 Ivailo Monev
+**
+** This file is part of the test suite of the Katie Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.h>
+#include <qreadwritelock.h>
+
+#if defined(Q_OS_SYMBIAN)
+// Symbian Open C has a bug that causes very short waits to fail sometimes
+#define COND_WAIT_TIME 50
+#else
+#define COND_WAIT_TIME 1
+#endif
+
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+class tst_QWaitCondition : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QWaitCondition();
+
+private slots:
+ void wait_QMutex();
+ void wait_QReadWriteLock();
+ void wakeOne();
+ void wakeAll();
+ void wait_RaceCondition();
+};
+
+static const int iterations = 10;
+
+// Note: some tests rely on ThreadCount being multiple of 2
+#if defined(Q_OS_SOLARIS) || ( defined(Q_OS_LINUX) && defined(QT_ARCH_ARMV6) )
+static const int ThreadCount = 4;
+#else
+static const int ThreadCount = 10;
+#endif
+
+tst_QWaitCondition::tst_QWaitCondition()
+
+{
+}
+
+class wait_QMutex_Thread_1 : public QThread
+{
+public:
+ QMutex mutex;
+ QWaitCondition cond;
+
+ inline wait_QMutex_Thread_1()
+ { }
+
+ void run()
+ {
+ mutex.lock();
+ cond.wakeOne();
+ cond.wait(&mutex);
+ mutex.unlock();
+ }
+};
+
+class wait_QMutex_Thread_2 : public QThread
+{
+public:
+ QWaitCondition started;
+
+ QMutex *mutex;
+ QWaitCondition *cond;
+
+ inline wait_QMutex_Thread_2()
+ : mutex(0), cond(0)
+ { }
+
+ void run()
+ {
+ mutex->lock();
+ started.wakeOne();
+ cond->wait(mutex);
+ mutex->unlock();
+ }
+};
+
+class wait_QReadWriteLock_Thread_1 : public QThread
+{
+public:
+ QReadWriteLock readWriteLock;
+ QWaitCondition cond;
+
+ inline wait_QReadWriteLock_Thread_1()
+ { }
+
+ void run()
+ {
+ readWriteLock.lockForWrite();
+ cond.wakeOne();
+ cond.wait(&readWriteLock);
+ readWriteLock.unlock();
+ }
+};
+
+class wait_QReadWriteLock_Thread_2 : public QThread
+{
+public:
+ QWaitCondition started;
+
+ QReadWriteLock *readWriteLock;
+ QWaitCondition *cond;
+
+ inline wait_QReadWriteLock_Thread_2()
+ : readWriteLock(0), cond(0)
+ { }
+
+ void run()
+ {
+ readWriteLock->lockForRead();
+ started.wakeOne();
+ cond->wait(readWriteLock);
+ readWriteLock->unlock();
+ }
+};
+
+void tst_QWaitCondition::wait_QMutex()
+{
+ int x;
+ for (int i = 0; i < iterations; ++i) {
+ {
+ QMutex mutex;
+ QWaitCondition cond;
+
+ mutex.lock();
+
+ cond.wakeOne();
+ QVERIFY(!cond.wait(&mutex, 1));
+
+ cond.wakeAll();
+ QVERIFY(!cond.wait(&mutex, 1));
+
+ mutex.unlock();
+ }
+
+ {
+ // test multiple threads waiting on separate wait conditions
+ wait_QMutex_Thread_1 thread[ThreadCount];
+
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].mutex.lock();
+ thread[x].start();
+ // wait for thread to start
+ QVERIFY(thread[x].cond.wait(&thread[x].mutex, 1000));
+ thread[x].mutex.unlock();
+ }
+
+ for (x = 0; x < ThreadCount; ++x) {
+ QVERIFY(thread[x].isRunning());
+ QVERIFY(!thread[x].isFinished());
+ }
+
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].mutex.lock();
+ thread[x].cond.wakeOne();
+ thread[x].mutex.unlock();
+ }
+
+ for (x = 0; x < ThreadCount; ++x) {
+ QVERIFY(thread[x].wait(1000));
+ }
+ }
+
+ {
+ // test multiple threads waiting on a wait condition
+ QMutex mutex;
+ QWaitCondition cond1, cond2;
+ wait_QMutex_Thread_2 thread[ThreadCount];
+
+ mutex.lock();
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].mutex = &mutex;
+ thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2;
+ thread[x].start();
+ // wait for thread to start
+ QVERIFY(thread[x].started.wait(&mutex, 1000));
+ }
+ mutex.unlock();
+
+ for (x = 0; x < ThreadCount; ++x) {
+ QVERIFY(thread[x].isRunning());
+ QVERIFY(!thread[x].isFinished());
+ }
+
+ mutex.lock();
+ cond1.wakeAll();
+ cond2.wakeAll();
+ mutex.unlock();
+
+ for (x = 0; x < ThreadCount; ++x) {
+ QVERIFY(thread[x].wait(1000));
+ }
+ }
+ }
+}
+
+void tst_QWaitCondition::wait_QReadWriteLock()
+{
+ {
+ QReadWriteLock readWriteLock(QReadWriteLock::Recursive);
+ QWaitCondition waitCondition;
+
+ // ensure that the lockForRead is correctly restored
+ readWriteLock.lockForRead();
+
+ QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+ QVERIFY(!readWriteLock.tryLockForWrite());
+ QVERIFY(readWriteLock.tryLockForRead());
+ readWriteLock.unlock();
+ QVERIFY(!readWriteLock.tryLockForWrite());
+ readWriteLock.unlock();
+
+ QVERIFY(readWriteLock.tryLockForWrite());
+ readWriteLock.unlock();
+ }
+
+ {
+ QReadWriteLock readWriteLock(QReadWriteLock::Recursive);
+ QWaitCondition waitCondition;
+
+ // ensure that the lockForWrite is correctly restored
+ readWriteLock.lockForWrite();
+
+ QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+ QVERIFY(!readWriteLock.tryLockForRead());
+ QVERIFY(readWriteLock.tryLockForWrite());
+ readWriteLock.unlock();
+ QVERIFY(!readWriteLock.tryLockForRead());
+ readWriteLock.unlock();
+
+ QVERIFY(readWriteLock.tryLockForRead());
+ readWriteLock.unlock();
+ }
+
+
+ int x;
+ for (int i = 0; i < iterations; ++i) {
+ {
+ QReadWriteLock readWriteLock;
+ QWaitCondition waitCondition;
+
+ readWriteLock.lockForRead();
+
+ waitCondition.wakeOne();
+ QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+ waitCondition.wakeAll();
+ QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+ readWriteLock.unlock();
+ }
+
+ {
+ QReadWriteLock readWriteLock;
+ QWaitCondition waitCondition;
+
+ readWriteLock.lockForWrite();
+
+ waitCondition.wakeOne();
+ QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+ waitCondition.wakeAll();
+ QVERIFY(!waitCondition.wait(&readWriteLock, 1));
+
+ readWriteLock.unlock();
+ }
+
+ {
+ // test multiple threads waiting on separate wait conditions
+ wait_QReadWriteLock_Thread_1 thread[ThreadCount];
+
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].readWriteLock.lockForRead();
+ thread[x].start();
+ // wait for thread to start
+#if defined(Q_OS_SYMBIAN) && defined(Q_CC_WINSCW)
+ // Symbian emulator startup simultaneously with this thread causes additional delay
+ QVERIFY(thread[x].cond.wait(&thread[x].readWriteLock, 10000));
+#else
+ QVERIFY(thread[x].cond.wait(&thread[x].readWriteLock, 1000));
+#endif
+ thread[x].readWriteLock.unlock();
+ }
+
+ for (x = 0; x < ThreadCount; ++x) {
+ QVERIFY(thread[x].isRunning());
+ QVERIFY(!thread[x].isFinished());
+ }
+
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].readWriteLock.lockForRead();
+ thread[x].cond.wakeOne();
+ thread[x].readWriteLock.unlock();
+ }
+
+ for (x = 0; x < ThreadCount; ++x) {
+ QVERIFY(thread[x].wait(1000));
+ }
+ }
+
+ {
+ // test multiple threads waiting on a wait condition
+ QReadWriteLock readWriteLock;
+ QWaitCondition cond1, cond2;
+ wait_QReadWriteLock_Thread_2 thread[ThreadCount];
+
+ readWriteLock.lockForWrite();
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].readWriteLock = &readWriteLock;
+ thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2;
+ thread[x].start();
+ // wait for thread to start
+ QVERIFY(thread[x].started.wait(&readWriteLock, 1000));
+ }
+ readWriteLock.unlock();
+
+ for (x = 0; x < ThreadCount; ++x) {
+ QVERIFY(thread[x].isRunning());
+ QVERIFY(!thread[x].isFinished());
+ }
+
+ readWriteLock.lockForWrite();
+ cond1.wakeAll();
+ cond2.wakeAll();
+ readWriteLock.unlock();
+
+ for (x = 0; x < ThreadCount; ++x) {
+ QVERIFY(thread[x].wait(1000));
+ }
+ }
+ }
+
+}
+
+class wake_Thread : public QThread
+{
+public:
+ static int count;
+
+ QWaitCondition started;
+ QWaitCondition dummy;
+
+ QMutex *mutex;
+ QWaitCondition *cond;
+
+ inline wake_Thread()
+ : mutex(0), cond(0)
+ { }
+
+ static inline void sleep(ulong s)
+ { QThread::sleep(s); }
+
+ void run()
+ {
+ mutex->lock();
+ ++count;
+ dummy.wakeOne(); // this wakeup should be lost
+ started.wakeOne();
+ dummy.wakeAll(); // this one too
+ cond->wait(mutex);
+ --count;
+ mutex->unlock();
+ }
+};
+
+int wake_Thread::count = 0;
+
+class wake_Thread_2 : public QThread
+{
+public:
+ static int count;
+
+ QWaitCondition started;
+ QWaitCondition dummy;
+
+ QReadWriteLock *readWriteLock;
+ QWaitCondition *cond;
+
+ inline wake_Thread_2()
+ : readWriteLock(0), cond(0)
+ { }
+
+ static inline void sleep(ulong s)
+ { QThread::sleep(s); }
+
+ void run()
+ {
+ readWriteLock->lockForWrite();
+ ++count;
+ dummy.wakeOne(); // this wakeup should be lost
+ started.wakeOne();
+ dummy.wakeAll(); // this one too
+ cond->wait(readWriteLock);
+ --count;
+ readWriteLock->unlock();
+ }
+};
+
+int wake_Thread_2::count = 0;
+
+void tst_QWaitCondition::wakeOne()
+{
+ int x;
+ // wake up threads, one at a time
+ for (int i = 0; i < iterations; ++i) {
+ QMutex mutex;
+ QWaitCondition cond;
+
+ // QMutex
+ wake_Thread thread[ThreadCount];
+ bool thread_exited[ThreadCount];
+
+ mutex.lock();
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].mutex = &mutex;
+ thread[x].cond = &cond;
+ thread_exited[x] = false;
+ thread[x].start();
+ // wait for thread to start
+ QVERIFY(thread[x].started.wait(&mutex, 1000));
+ // make sure wakeups are not queued... if nothing is
+ // waiting at the time of the wakeup, nothing happens
+ QVERIFY(!thread[x].dummy.wait(&mutex, 1));
+ }
+ mutex.unlock();
+
+ QCOMPARE(wake_Thread::count, ThreadCount);
+
+ // wake up threads one at a time
+ for (x = 0; x < ThreadCount; ++x) {
+ mutex.lock();
+ cond.wakeOne();
+ QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
+ QVERIFY(!thread[x].dummy.wait(&mutex, 1));
+ mutex.unlock();
+
+ int exited = 0;
+ for (int y = 0; y < ThreadCount; ++y) {
+ if (thread_exited[y])
+ continue;
+ if (thread[y].wait(exited > 0 ? 3 : 1000)) {
+ thread_exited[y] = true;
+ ++exited;
+ }
+ }
+
+ QCOMPARE(exited, 1);
+ QCOMPARE(wake_Thread::count, ThreadCount - (x + 1));
+ }
+
+ QCOMPARE(wake_Thread::count, 0);
+
+ // QReadWriteLock
+ QReadWriteLock readWriteLock;
+ wake_Thread_2 rwthread[ThreadCount];
+
+ readWriteLock.lockForWrite();
+ for (x = 0; x < ThreadCount; ++x) {
+ rwthread[x].readWriteLock = &readWriteLock;
+ rwthread[x].cond = &cond;
+ thread_exited[x] = false;
+ rwthread[x].start();
+ // wait for thread to start
+ QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
+ // make sure wakeups are not queued... if nothing is
+ // waiting at the time of the wakeup, nothing happens
+ QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
+ }
+ readWriteLock.unlock();
+
+ QCOMPARE(wake_Thread_2::count, ThreadCount);
+
+ // wake up threads one at a time
+ for (x = 0; x < ThreadCount; ++x) {
+ readWriteLock.lockForWrite();
+ cond.wakeOne();
+ QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
+ QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
+ readWriteLock.unlock();
+
+ int exited = 0;
+ for (int y = 0; y < ThreadCount; ++y) {
+ if (thread_exited[y])
+ continue;
+ if (rwthread[y].wait(exited > 0 ? 3 : 1000)) {
+ thread_exited[y] = true;
+ ++exited;
+ }
+ }
+
+ QCOMPARE(exited, 1);
+ QCOMPARE(wake_Thread_2::count, ThreadCount - (x + 1));
+ }
+
+ QCOMPARE(wake_Thread_2::count, 0);
+ }
+
+ // wake up threads, two at a time
+ for (int i = 0; i < iterations; ++i) {
+ QMutex mutex;
+ QWaitCondition cond;
+
+ // QMutex
+ wake_Thread thread[ThreadCount];
+ bool thread_exited[ThreadCount];
+
+ mutex.lock();
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].mutex = &mutex;
+ thread[x].cond = &cond;
+ thread_exited[x] = false;
+ thread[x].start();
+ // wait for thread to start
+ QVERIFY(thread[x].started.wait(&mutex, 1000));
+ // make sure wakeups are not queued... if nothing is
+ // waiting at the time of the wakeup, nothing happens
+ QVERIFY(!thread[x].dummy.wait(&mutex, 1));
+ }
+ mutex.unlock();
+
+ QCOMPARE(wake_Thread::count, ThreadCount);
+
+ // wake up threads one at a time
+ for (x = 0; x < ThreadCount; x += 2) {
+ mutex.lock();
+ cond.wakeOne();
+ cond.wakeOne();
+ QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
+ QVERIFY(!thread[x].dummy.wait(&mutex, 1));
+ QVERIFY(!thread[x + 1].dummy.wait(&mutex, 1));
+ mutex.unlock();
+
+ int exited = 0;
+ for (int y = 0; y < ThreadCount; ++y) {
+ if (thread_exited[y])
+ continue;
+ if (thread[y].wait(exited > 0 ? 3 : 1000)) {
+ thread_exited[y] = true;
+ ++exited;
+ }
+ }
+
+ QCOMPARE(exited, 2);
+ QCOMPARE(wake_Thread::count, ThreadCount - (x + 2));
+ }
+
+ QCOMPARE(wake_Thread::count, 0);
+
+ // QReadWriteLock
+ QReadWriteLock readWriteLock;
+ wake_Thread_2 rwthread[ThreadCount];
+
+ readWriteLock.lockForWrite();
+ for (x = 0; x < ThreadCount; ++x) {
+ rwthread[x].readWriteLock = &readWriteLock;
+ rwthread[x].cond = &cond;
+ thread_exited[x] = false;
+ rwthread[x].start();
+ // wait for thread to start
+ QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
+ // make sure wakeups are not queued... if nothing is
+ // waiting at the time of the wakeup, nothing happens
+ QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
+ }
+ readWriteLock.unlock();
+
+ QCOMPARE(wake_Thread_2::count, ThreadCount);
+
+ // wake up threads one at a time
+ for (x = 0; x < ThreadCount; x += 2) {
+ readWriteLock.lockForWrite();
+ cond.wakeOne();
+ cond.wakeOne();
+ QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
+ QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
+ QVERIFY(!rwthread[x + 1].dummy.wait(&readWriteLock, 1));
+ readWriteLock.unlock();
+
+ int exited = 0;
+ for (int y = 0; y < ThreadCount; ++y) {
+ if (thread_exited[y])
+ continue;
+ if (rwthread[y].wait(exited > 0 ? 3 : 1000)) {
+ thread_exited[y] = true;
+ ++exited;
+ }
+ }
+
+ QCOMPARE(exited, 2);
+ QCOMPARE(wake_Thread_2::count, ThreadCount - (x + 2));
+ }
+
+ QCOMPARE(wake_Thread_2::count, 0);
+}
+}
+
+void tst_QWaitCondition::wakeAll()
+{
+ int x;
+ for (int i = 0; i < iterations; ++i) {
+ QMutex mutex;
+ QWaitCondition cond;
+
+ // QMutex
+ wake_Thread thread[ThreadCount];
+
+ mutex.lock();
+ for (x = 0; x < ThreadCount; ++x) {
+ thread[x].mutex = &mutex;
+ thread[x].cond = &cond;
+ thread[x].start();
+ // wait for thread to start
+ QVERIFY(thread[x].started.wait(&mutex, 1000));
+ }
+ mutex.unlock();
+
+ QCOMPARE(wake_Thread::count, ThreadCount);
+
+ // wake up all threads at once
+ mutex.lock();
+ cond.wakeAll();
+ QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
+ mutex.unlock();
+
+ int exited = 0;
+ for (x = 0; x < ThreadCount; ++x) {
+ if (thread[x].wait(1000))
+ ++exited;
+ }
+
+ QCOMPARE(exited, ThreadCount);
+ QCOMPARE(wake_Thread::count, 0);
+
+ // QReadWriteLock
+ QReadWriteLock readWriteLock;
+ wake_Thread_2 rwthread[ThreadCount];
+
+ readWriteLock.lockForWrite();
+ for (x = 0; x < ThreadCount; ++x) {
+ rwthread[x].readWriteLock = &readWriteLock;
+ rwthread[x].cond = &cond;
+ rwthread[x].start();
+ // wait for thread to start
+ QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
+ }
+ readWriteLock.unlock();
+
+ QCOMPARE(wake_Thread_2::count, ThreadCount);
+
+ // wake up all threads at once
+ readWriteLock.lockForWrite();
+ cond.wakeAll();
+ QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
+ readWriteLock.unlock();
+
+ exited = 0;
+ for (x = 0; x < ThreadCount; ++x) {
+ if (rwthread[x].wait(1000))
+ ++exited;
+ }
+
+ QCOMPARE(exited, ThreadCount);
+ QCOMPARE(wake_Thread_2::count, 0);
+ }
+}
+
+class wait_RaceConditionThread : public QThread
+{
+public:
+ wait_RaceConditionThread(QMutex *mutex, QWaitCondition *startup, QWaitCondition *waitCondition,
+ ulong timeout = ULONG_MAX)
+ : timeout(timeout), returnValue(false), ready(false),
+ mutex(mutex), startup(startup), waitCondition(waitCondition) {}
+
+ unsigned long timeout;
+ bool returnValue;
+
+ bool ready;
+
+ QMutex *mutex;
+ QWaitCondition *startup;
+ QWaitCondition *waitCondition;
+
+ void run() {
+ mutex->lock();
+
+ ready = true;
+ startup->wakeOne();
+
+ returnValue = waitCondition->wait(mutex, timeout);
+
+ mutex->unlock();
+ }
+};
+
+class wait_RaceConditionThread_2 : public QThread
+{
+public:
+ wait_RaceConditionThread_2(QReadWriteLock *readWriteLock,
+ QWaitCondition *startup,
+ QWaitCondition *waitCondition,
+ ulong timeout = ULONG_MAX)
+ : timeout(timeout), returnValue(false), ready(false),
+ readWriteLock(readWriteLock), startup(startup), waitCondition(waitCondition)
+ { }
+
+ unsigned long timeout;
+ bool returnValue;
+
+ bool ready;
+
+ QReadWriteLock *readWriteLock;
+ QWaitCondition *startup;
+ QWaitCondition *waitCondition;
+
+ void run() {
+ readWriteLock->lockForWrite();
+
+ ready = true;
+ startup->wakeOne();
+
+ returnValue = waitCondition->wait(readWriteLock, timeout);
+
+ readWriteLock->unlock();
+ }
+};
+
+void tst_QWaitCondition::wait_RaceCondition()
+{
+ {
+ QMutex mutex;
+ QWaitCondition startup;
+ QWaitCondition waitCondition;
+
+ wait_RaceConditionThread timeoutThread(&mutex, &startup, &waitCondition, 1000),
+ waitingThread1(&mutex, &startup, &waitCondition);
+
+ timeoutThread.start();
+ waitingThread1.start();
+ mutex.lock();
+
+ // wait for the threads to start up
+ while (!timeoutThread.ready
+ || !waitingThread1.ready) {
+ startup.wait(&mutex);
+ }
+
+ QTest::qWait(2000);
+
+ waitCondition.wakeOne();
+
+ mutex.unlock();
+
+ QVERIFY(timeoutThread.wait(5000));
+ QVERIFY(!timeoutThread.returnValue);
+ QVERIFY(waitingThread1.wait(5000));
+ QVERIFY(waitingThread1.returnValue);
+ }
+
+ {
+ QReadWriteLock readWriteLock;
+ QWaitCondition startup;
+ QWaitCondition waitCondition;
+
+ wait_RaceConditionThread_2 timeoutThread(&readWriteLock, &startup, &waitCondition, 1000),
+ waitingThread1(&readWriteLock, &startup, &waitCondition);
+
+ timeoutThread.start();
+ waitingThread1.start();
+ readWriteLock.lockForRead();
+
+ // wait for the threads to start up
+ while (!timeoutThread.ready
+ || !waitingThread1.ready) {
+ startup.wait(&readWriteLock);
+ }
+
+ QTest::qWait(2000);
+
+ waitCondition.wakeOne();
+
+ readWriteLock.unlock();
+
+ QVERIFY(timeoutThread.wait(5000));
+ QVERIFY(!timeoutThread.returnValue);
+ QVERIFY(waitingThread1.wait(5000));
+ QVERIFY(waitingThread1.returnValue);
+ }
+}
+
+QTEST_MAIN(tst_QWaitCondition)
+
+#include "moc_tst_qwaitcondition.cpp"