summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/thread/qwaitcondition
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/thread/qwaitcondition')
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/.gitignore1
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro6
-rw-r--r--tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp845
3 files changed, 852 insertions, 0 deletions
diff --git a/tests/auto/corelib/thread/qwaitcondition/.gitignore b/tests/auto/corelib/thread/qwaitcondition/.gitignore
new file mode 100644
index 0000000000..96531988d9
--- /dev/null
+++ b/tests/auto/corelib/thread/qwaitcondition/.gitignore
@@ -0,0 +1 @@
+tst_qwaitcondition
diff --git a/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro b/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro
new file mode 100644
index 0000000000..9af0c71301
--- /dev/null
+++ b/tests/auto/corelib/thread/qwaitcondition/qwaitcondition.pro
@@ -0,0 +1,6 @@
+load(qttest_p4)
+SOURCES += tst_qwaitcondition.cpp
+QT = core
+
+
+CONFIG += parallel_test
diff --git a/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
new file mode 100644
index 0000000000..6545df9189
--- /dev/null
+++ b/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp
@@ -0,0 +1,845 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qwaitcondition.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 ? 10 : 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 ? 10 : 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 ? 10 : 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 ? 10 : 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 "tst_qwaitcondition.moc"