38be0d1383
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
846 lines
21 KiB
C++
846 lines
21 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 <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 ? 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 "tst_qwaitcondition.moc"
|