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