qt5base-lts/tests/auto/qwaitcondition/tst_qwaitcondition.cpp
Qt by Nokia 38be0d1383 Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you
want to look at revision history older than this, please refer to the
Qt Git wiki for how to use Git history grafting. At the time of
writing, this wiki is located here:

http://qt.gitorious.org/qt/pages/GitIntroductionWithQt

If you have already performed the grafting and you don't see any
history beyond this commit, try running "git log" with the "--follow"
argument.

Branched from the monolithic repo, Qt master branch, at commit
896db169ea224deb96c59ce8af800d019de63f12
2011-04-27 12:05:43 +02:00

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"