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
1128 lines
27 KiB
C++
1128 lines
27 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 <qreadwritelock.h>
|
|
#include <qmutex.h>
|
|
#include <qthread.h>
|
|
#include <qwaitcondition.h>
|
|
|
|
#ifdef Q_OS_UNIX
|
|
#include <unistd.h>
|
|
#endif
|
|
#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
|
|
#include <windows.h>
|
|
#define sleep(X) Sleep(X)
|
|
#endif
|
|
|
|
//on solaris, threads that loop one the release bool variable
|
|
//needs to sleep more than 1 usec.
|
|
#ifdef Q_OS_SOLARIS
|
|
# define RWTESTSLEEP usleep(10);
|
|
#else
|
|
# define RWTESTSLEEP usleep(1);
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
//TESTED_CLASS=
|
|
//TESTED_FILES=
|
|
|
|
class tst_QReadWriteLock : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
tst_QReadWriteLock();
|
|
virtual ~tst_QReadWriteLock();
|
|
|
|
|
|
/*
|
|
Singlethreaded tests
|
|
*/
|
|
private slots:
|
|
void constructDestruct();
|
|
void readLockUnlock();
|
|
void writeLockUnlock();
|
|
void readLockUnlockLoop();
|
|
void writeLockUnlockLoop();
|
|
void readLockLoop();
|
|
void writeLockLoop();
|
|
void readWriteLockUnlockLoop();
|
|
void tryReadLock();
|
|
void tryWriteLock();
|
|
/*
|
|
Multithreaded tests
|
|
*/
|
|
private slots:
|
|
|
|
void readLockBlockRelease();
|
|
void writeLockBlockRelease();
|
|
void multipleReadersBlockRelease();
|
|
void multipleReadersLoop();
|
|
void multipleWritersLoop();
|
|
void multipleReadersWritersLoop();
|
|
void countingTest();
|
|
void limitedReaders();
|
|
void deleteOnUnlock();
|
|
|
|
/*
|
|
Performance tests
|
|
*/
|
|
private slots:
|
|
void uncontendedLocks();
|
|
|
|
// recursive locking tests
|
|
void recursiveReadLock();
|
|
void recursiveWriteLock();
|
|
};
|
|
|
|
tst_QReadWriteLock::tst_QReadWriteLock()
|
|
{
|
|
|
|
}
|
|
|
|
tst_QReadWriteLock::~tst_QReadWriteLock()
|
|
{
|
|
|
|
}
|
|
|
|
void tst_QReadWriteLock::constructDestruct()
|
|
{
|
|
{
|
|
QReadWriteLock rwlock;
|
|
}
|
|
}
|
|
|
|
void tst_QReadWriteLock::readLockUnlock()
|
|
{
|
|
QReadWriteLock rwlock;
|
|
rwlock.lockForRead();
|
|
rwlock.unlock();
|
|
}
|
|
|
|
void tst_QReadWriteLock::writeLockUnlock()
|
|
{
|
|
QReadWriteLock rwlock;
|
|
rwlock.lockForWrite();
|
|
rwlock.unlock();
|
|
}
|
|
|
|
void tst_QReadWriteLock::readLockUnlockLoop()
|
|
{
|
|
QReadWriteLock rwlock;
|
|
int runs=10000;
|
|
int i;
|
|
for (i=0; i<runs; ++i) {
|
|
rwlock.lockForRead();
|
|
rwlock.unlock();
|
|
}
|
|
}
|
|
|
|
void tst_QReadWriteLock::writeLockUnlockLoop()
|
|
{
|
|
QReadWriteLock rwlock;
|
|
int runs=10000;
|
|
int i;
|
|
for (i=0; i<runs; ++i) {
|
|
rwlock.lockForWrite();
|
|
rwlock.unlock();
|
|
}
|
|
}
|
|
|
|
|
|
void tst_QReadWriteLock::readLockLoop()
|
|
{
|
|
QReadWriteLock rwlock;
|
|
int runs=10000;
|
|
int i;
|
|
for (i=0; i<runs; ++i) {
|
|
rwlock.lockForRead();
|
|
}
|
|
for (i=0; i<runs; ++i) {
|
|
rwlock.unlock();
|
|
}
|
|
}
|
|
|
|
void tst_QReadWriteLock::writeLockLoop()
|
|
{
|
|
/*
|
|
If you include this, the test should print one line
|
|
and then block.
|
|
*/
|
|
#if 0
|
|
QReadWriteLock rwlock;
|
|
int runs=10000;
|
|
int i;
|
|
for (i=0; i<runs; ++i) {
|
|
rwlock.lockForWrite();
|
|
qDebug("I am going to block now.");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void tst_QReadWriteLock::readWriteLockUnlockLoop()
|
|
{
|
|
QReadWriteLock rwlock;
|
|
int runs=10000;
|
|
int i;
|
|
for (i=0; i<runs; ++i) {
|
|
rwlock.lockForRead();
|
|
rwlock.unlock();
|
|
rwlock.lockForWrite();
|
|
rwlock.unlock();
|
|
}
|
|
|
|
}
|
|
|
|
QAtomicInt lockCount(0);
|
|
QReadWriteLock readWriteLock;
|
|
QSemaphore testsTurn;
|
|
QSemaphore threadsTurn;
|
|
|
|
|
|
void tst_QReadWriteLock::tryReadLock()
|
|
{
|
|
QReadWriteLock rwlock;
|
|
QVERIFY(rwlock.tryLockForRead());
|
|
rwlock.unlock();
|
|
QVERIFY(rwlock.tryLockForRead());
|
|
rwlock.unlock();
|
|
|
|
rwlock.lockForRead();
|
|
rwlock.lockForRead();
|
|
QVERIFY(rwlock.tryLockForRead());
|
|
rwlock.unlock();
|
|
rwlock.unlock();
|
|
rwlock.unlock();
|
|
|
|
rwlock.lockForWrite();
|
|
QVERIFY(!rwlock.tryLockForRead());
|
|
rwlock.unlock();
|
|
|
|
// functionality test
|
|
{
|
|
class Thread : public QThread
|
|
{
|
|
public:
|
|
void run()
|
|
{
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
QVERIFY(!readWriteLock.tryLockForRead());
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
QVERIFY(readWriteLock.tryLockForRead());
|
|
lockCount.ref();
|
|
QVERIFY(readWriteLock.tryLockForRead());
|
|
lockCount.ref();
|
|
lockCount.deref();
|
|
readWriteLock.unlock();
|
|
lockCount.deref();
|
|
readWriteLock.unlock();
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
QTime timer;
|
|
timer.start();
|
|
QVERIFY(!readWriteLock.tryLockForRead(1000));
|
|
QVERIFY(timer.elapsed() >= 1000);
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
timer.start();
|
|
QVERIFY(readWriteLock.tryLockForRead(1000));
|
|
QVERIFY(timer.elapsed() <= 1000);
|
|
lockCount.ref();
|
|
QVERIFY(readWriteLock.tryLockForRead(1000));
|
|
lockCount.ref();
|
|
lockCount.deref();
|
|
readWriteLock.unlock();
|
|
lockCount.deref();
|
|
readWriteLock.unlock();
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
}
|
|
};
|
|
|
|
Thread thread;
|
|
thread.start();
|
|
|
|
testsTurn.acquire();
|
|
readWriteLock.lockForWrite();
|
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
|
readWriteLock.unlock();
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
readWriteLock.lockForWrite();
|
|
QVERIFY(lockCount.testAndSetRelaxed(0, 1));
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
QVERIFY(lockCount.testAndSetRelaxed(1, 0));
|
|
readWriteLock.unlock();
|
|
threadsTurn.release();
|
|
|
|
// stop thread
|
|
testsTurn.acquire();
|
|
threadsTurn.release();
|
|
thread.wait();
|
|
}
|
|
}
|
|
|
|
void tst_QReadWriteLock::tryWriteLock()
|
|
{
|
|
{
|
|
QReadWriteLock rwlock;
|
|
QVERIFY(rwlock.tryLockForWrite());
|
|
rwlock.unlock();
|
|
QVERIFY(rwlock.tryLockForWrite());
|
|
rwlock.unlock();
|
|
|
|
rwlock.lockForWrite();
|
|
QVERIFY(!rwlock.tryLockForWrite());
|
|
QVERIFY(!rwlock.tryLockForWrite());
|
|
rwlock.unlock();
|
|
|
|
rwlock.lockForRead();
|
|
QVERIFY(!rwlock.tryLockForWrite());
|
|
rwlock.unlock();
|
|
}
|
|
|
|
{
|
|
QReadWriteLock rwlock(QReadWriteLock::Recursive);
|
|
QVERIFY(rwlock.tryLockForWrite());
|
|
rwlock.unlock();
|
|
QVERIFY(rwlock.tryLockForWrite());
|
|
rwlock.unlock();
|
|
|
|
rwlock.lockForWrite();
|
|
QVERIFY(rwlock.tryLockForWrite());
|
|
QVERIFY(rwlock.tryLockForWrite());
|
|
rwlock.unlock();
|
|
rwlock.unlock();
|
|
rwlock.unlock();
|
|
|
|
rwlock.lockForRead();
|
|
QVERIFY(!rwlock.tryLockForWrite());
|
|
rwlock.unlock();
|
|
}
|
|
|
|
// functionality test
|
|
{
|
|
class Thread : public QThread
|
|
{
|
|
public:
|
|
void run()
|
|
{
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
Q_ASSERT(!readWriteLock.tryLockForWrite());
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
Q_ASSERT(readWriteLock.tryLockForWrite());
|
|
Q_ASSERT(lockCount.testAndSetRelaxed(0, 1));
|
|
Q_ASSERT(lockCount.testAndSetRelaxed(1, 0));
|
|
readWriteLock.unlock();
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
Q_ASSERT(!readWriteLock.tryLockForWrite(1000));
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
Q_ASSERT(readWriteLock.tryLockForWrite(1000));
|
|
Q_ASSERT(lockCount.testAndSetRelaxed(0, 1));
|
|
Q_ASSERT(lockCount.testAndSetRelaxed(1, 0));
|
|
readWriteLock.unlock();
|
|
testsTurn.release();
|
|
|
|
threadsTurn.acquire();
|
|
}
|
|
};
|
|
|
|
Thread thread;
|
|
thread.start();
|
|
|
|
testsTurn.acquire();
|
|
readWriteLock.lockForRead();
|
|
lockCount.ref();
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
lockCount.deref();
|
|
readWriteLock.unlock();
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
readWriteLock.lockForRead();
|
|
lockCount.ref();
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
lockCount.deref();
|
|
readWriteLock.unlock();
|
|
threadsTurn.release();
|
|
|
|
// stop thread
|
|
testsTurn.acquire();
|
|
threadsTurn.release();
|
|
thread.wait();
|
|
}
|
|
}
|
|
|
|
bool threadDone;
|
|
volatile bool release;
|
|
|
|
/*
|
|
write-lock
|
|
unlock
|
|
set threadone
|
|
*/
|
|
class WriteLockThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock &testRwlock;
|
|
inline WriteLockThread(QReadWriteLock &l) : testRwlock(l) { }
|
|
void run()
|
|
{
|
|
testRwlock.lockForWrite();
|
|
testRwlock.unlock();
|
|
threadDone=true;
|
|
}
|
|
};
|
|
|
|
/*
|
|
read-lock
|
|
unlock
|
|
set threadone
|
|
*/
|
|
class ReadLockThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock &testRwlock;
|
|
inline ReadLockThread(QReadWriteLock &l) : testRwlock(l) { }
|
|
void run()
|
|
{
|
|
testRwlock.lockForRead();
|
|
testRwlock.unlock();
|
|
threadDone=true;
|
|
}
|
|
};
|
|
/*
|
|
write-lock
|
|
wait for release==true
|
|
unlock
|
|
*/
|
|
class WriteLockReleasableThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock &testRwlock;
|
|
inline WriteLockReleasableThread(QReadWriteLock &l) : testRwlock(l) { }
|
|
void run()
|
|
{
|
|
testRwlock.lockForWrite();
|
|
while(release==false) {
|
|
RWTESTSLEEP
|
|
}
|
|
testRwlock.unlock();
|
|
}
|
|
};
|
|
|
|
/*
|
|
read-lock
|
|
wait for release==true
|
|
unlock
|
|
*/
|
|
class ReadLockReleasableThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock &testRwlock;
|
|
inline ReadLockReleasableThread(QReadWriteLock &l) : testRwlock(l) { }
|
|
void run()
|
|
{
|
|
testRwlock.lockForRead();
|
|
while(release==false) {
|
|
RWTESTSLEEP
|
|
}
|
|
testRwlock.unlock();
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
for(runTime msecs)
|
|
read-lock
|
|
msleep(holdTime msecs)
|
|
release lock
|
|
msleep(waitTime msecs)
|
|
*/
|
|
class ReadLockLoopThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock &testRwlock;
|
|
int runTime;
|
|
int holdTime;
|
|
int waitTime;
|
|
bool print;
|
|
QTime t;
|
|
inline ReadLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false)
|
|
:testRwlock(l)
|
|
,runTime(runTime)
|
|
,holdTime(holdTime)
|
|
,waitTime(waitTime)
|
|
,print(print)
|
|
{ }
|
|
void run()
|
|
{
|
|
t.start();
|
|
while (t.elapsed()<runTime) {
|
|
testRwlock.lockForRead();
|
|
if(print) printf("reading\n");
|
|
if (holdTime) msleep(holdTime);
|
|
testRwlock.unlock();
|
|
if (waitTime) msleep(waitTime);
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
for(runTime msecs)
|
|
write-lock
|
|
msleep(holdTime msecs)
|
|
release lock
|
|
msleep(waitTime msecs)
|
|
*/
|
|
class WriteLockLoopThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock &testRwlock;
|
|
int runTime;
|
|
int holdTime;
|
|
int waitTime;
|
|
bool print;
|
|
QTime t;
|
|
inline WriteLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false)
|
|
:testRwlock(l)
|
|
,runTime(runTime)
|
|
,holdTime(holdTime)
|
|
,waitTime(waitTime)
|
|
,print(print)
|
|
{ }
|
|
void run()
|
|
{
|
|
t.start();
|
|
while (t.elapsed() < runTime) {
|
|
testRwlock.lockForWrite();
|
|
if (print) printf(".");
|
|
if (holdTime) msleep(holdTime);
|
|
testRwlock.unlock();
|
|
if (waitTime) msleep(waitTime);
|
|
}
|
|
}
|
|
};
|
|
|
|
volatile int count=0;
|
|
|
|
/*
|
|
for(runTime msecs)
|
|
write-lock
|
|
count to maxval
|
|
set count to 0
|
|
release lock
|
|
msleep waitTime
|
|
*/
|
|
class WriteLockCountThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock &testRwlock;
|
|
int runTime;
|
|
int waitTime;
|
|
int maxval;
|
|
QTime t;
|
|
inline WriteLockCountThread(QReadWriteLock &l, int runTime, int waitTime, int maxval)
|
|
:testRwlock(l)
|
|
,runTime(runTime)
|
|
,waitTime(waitTime)
|
|
,maxval(maxval)
|
|
{ }
|
|
void run()
|
|
{
|
|
t.start();
|
|
while (t.elapsed() < runTime) {
|
|
testRwlock.lockForWrite();
|
|
if(count)
|
|
qFatal("Non-zero count at start of write! (%d)",count );
|
|
// printf(".");
|
|
int i;
|
|
for(i=0; i<maxval; ++i) {
|
|
volatile int lc=count;
|
|
++lc;
|
|
count=lc;
|
|
}
|
|
count=0;
|
|
testRwlock.unlock();
|
|
msleep(waitTime);
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
for(runTime msecs)
|
|
read-lock
|
|
verify count==0
|
|
release lock
|
|
msleep waitTime
|
|
*/
|
|
class ReadLockCountThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock &testRwlock;
|
|
int runTime;
|
|
int waitTime;
|
|
QTime t;
|
|
inline ReadLockCountThread(QReadWriteLock &l, int runTime, int waitTime)
|
|
:testRwlock(l)
|
|
,runTime(runTime)
|
|
,waitTime(waitTime)
|
|
{ }
|
|
void run()
|
|
{
|
|
t.start();
|
|
while (t.elapsed() < runTime) {
|
|
testRwlock.lockForRead();
|
|
if(count)
|
|
qFatal("Non-zero count at Read! (%d)",count );
|
|
testRwlock.unlock();
|
|
msleep(waitTime);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
A writer aquires a read-lock, a reader locks
|
|
the writer releases the lock, the reader gets the lock
|
|
*/
|
|
void tst_QReadWriteLock::readLockBlockRelease()
|
|
{
|
|
QReadWriteLock testLock;
|
|
testLock.lockForWrite();
|
|
threadDone=false;
|
|
ReadLockThread rlt(testLock);
|
|
rlt.start();
|
|
sleep(1);
|
|
testLock.unlock();
|
|
rlt.wait();
|
|
QVERIFY(threadDone);
|
|
}
|
|
|
|
/*
|
|
writer1 aquires a read-lock, writer2 blocks,
|
|
writer1 releases the lock, writer2 gets the lock
|
|
*/
|
|
void tst_QReadWriteLock::writeLockBlockRelease()
|
|
{
|
|
QReadWriteLock testLock;
|
|
testLock.lockForWrite();
|
|
threadDone=false;
|
|
WriteLockThread wlt(testLock);
|
|
wlt.start();
|
|
sleep(1);
|
|
testLock.unlock();
|
|
wlt.wait();
|
|
QVERIFY(threadDone);
|
|
}
|
|
/*
|
|
Two readers aquire a read-lock, one writer attempts a write block,
|
|
the readers release their locks, the writer gets the lock.
|
|
*/
|
|
void tst_QReadWriteLock::multipleReadersBlockRelease()
|
|
{
|
|
|
|
QReadWriteLock testLock;
|
|
release=false;
|
|
threadDone=false;
|
|
ReadLockReleasableThread rlt1(testLock);
|
|
ReadLockReleasableThread rlt2(testLock);
|
|
rlt1.start();
|
|
rlt2.start();
|
|
sleep(1);
|
|
WriteLockThread wlt(testLock);
|
|
wlt.start();
|
|
sleep(1);
|
|
release=true;
|
|
wlt.wait();
|
|
rlt1.wait();
|
|
rlt2.wait();
|
|
QVERIFY(threadDone);
|
|
}
|
|
|
|
/*
|
|
Multiple readers locks and unlocks a lock.
|
|
*/
|
|
void tst_QReadWriteLock::multipleReadersLoop()
|
|
{
|
|
int time=500;
|
|
int hold=250;
|
|
int wait=0;
|
|
#if defined (Q_OS_HPUX)
|
|
const int numthreads=50;
|
|
#elif defined(Q_OS_VXWORKS)
|
|
const int numthreads=40;
|
|
#else
|
|
const int numthreads=75;
|
|
#endif
|
|
QReadWriteLock testLock;
|
|
ReadLockLoopThread *threads[numthreads];
|
|
int i;
|
|
for (i=0; i<numthreads; ++i)
|
|
threads[i] = new ReadLockLoopThread(testLock, time, hold, wait);
|
|
for (i=0; i<numthreads; ++i)
|
|
threads[i]->start();
|
|
for (i=0; i<numthreads; ++i)
|
|
threads[i]->wait();
|
|
for (i=0; i<numthreads; ++i)
|
|
delete threads[i];
|
|
}
|
|
|
|
/*
|
|
Multiple writers locks and unlocks a lock.
|
|
*/
|
|
void tst_QReadWriteLock::multipleWritersLoop()
|
|
{
|
|
int time=500;
|
|
int wait=0;
|
|
int hold=0;
|
|
const int numthreads=50;
|
|
QReadWriteLock testLock;
|
|
WriteLockLoopThread *threads[numthreads];
|
|
int i;
|
|
for (i=0; i<numthreads; ++i)
|
|
threads[i] = new WriteLockLoopThread(testLock, time, hold, wait);
|
|
for (i=0; i<numthreads; ++i)
|
|
threads[i]->start();
|
|
for (i=0; i<numthreads; ++i)
|
|
threads[i]->wait();
|
|
for (i=0; i<numthreads; ++i)
|
|
delete threads[i];
|
|
}
|
|
|
|
/*
|
|
Multiple readers and writers locks and unlocks a lock.
|
|
*/
|
|
void tst_QReadWriteLock::multipleReadersWritersLoop()
|
|
{
|
|
//int time=INT_MAX;
|
|
int time=10000;
|
|
int readerThreads=20;
|
|
int readerWait=0;
|
|
int readerHold=1;
|
|
|
|
int writerThreads=2;
|
|
int writerWait=500;
|
|
int writerHold=50;
|
|
|
|
QReadWriteLock testLock;
|
|
ReadLockLoopThread *readers[1024];
|
|
WriteLockLoopThread *writers[1024];
|
|
int i;
|
|
|
|
for (i=0; i<readerThreads; ++i)
|
|
readers[i] = new ReadLockLoopThread(testLock, time, readerHold, readerWait, false);
|
|
for (i=0; i<writerThreads; ++i)
|
|
writers[i] = new WriteLockLoopThread(testLock, time, writerHold, writerWait, false);
|
|
|
|
for (i=0; i<readerThreads; ++i)
|
|
readers[i]->start(QThread::NormalPriority);
|
|
for (i=0; i<writerThreads; ++i)
|
|
writers[i]->start(QThread::IdlePriority);
|
|
|
|
for (i=0; i<readerThreads; ++i)
|
|
readers[i]->wait();
|
|
for (i=0; i<writerThreads; ++i)
|
|
writers[i]->wait();
|
|
|
|
for (i=0; i<readerThreads; ++i)
|
|
delete readers[i];
|
|
for (i=0; i<writerThreads; ++i)
|
|
delete writers[i];
|
|
}
|
|
|
|
/*
|
|
Writers increment a variable from 0 to maxval, then reset it to 0.
|
|
Readers verify that the variable remains at 0.
|
|
*/
|
|
void tst_QReadWriteLock::countingTest()
|
|
{
|
|
//int time=INT_MAX;
|
|
int time=10000;
|
|
int readerThreads=20;
|
|
int readerWait=1;
|
|
|
|
int writerThreads=3;
|
|
int writerWait=150;
|
|
int maxval=10000;
|
|
|
|
QReadWriteLock testLock;
|
|
ReadLockCountThread *readers[1024];
|
|
WriteLockCountThread *writers[1024];
|
|
int i;
|
|
|
|
for (i=0; i<readerThreads; ++i)
|
|
readers[i] = new ReadLockCountThread(testLock, time, readerWait);
|
|
for (i=0; i<writerThreads; ++i)
|
|
writers[i] = new WriteLockCountThread(testLock, time, writerWait, maxval);
|
|
|
|
for (i=0; i<readerThreads; ++i)
|
|
readers[i]->start(QThread::NormalPriority);
|
|
for (i=0; i<writerThreads; ++i)
|
|
writers[i]->start(QThread::LowestPriority);
|
|
|
|
for (i=0; i<readerThreads; ++i)
|
|
readers[i]->wait();
|
|
for (i=0; i<writerThreads; ++i)
|
|
writers[i]->wait();
|
|
|
|
for (i=0; i<readerThreads; ++i)
|
|
delete readers[i];
|
|
for (i=0; i<writerThreads; ++i)
|
|
delete writers[i];
|
|
}
|
|
|
|
void tst_QReadWriteLock::limitedReaders()
|
|
{
|
|
|
|
};
|
|
|
|
/*
|
|
Test a race-condition that may happen if one thread is in unlock() while
|
|
another thread deletes the rw-lock.
|
|
|
|
MainThread DeleteOnUnlockThread
|
|
|
|
write-lock
|
|
unlock
|
|
| write-lock
|
|
| unlock
|
|
| delete lock
|
|
deref d inside unlock
|
|
*/
|
|
class DeleteOnUnlockThread : public QThread
|
|
{
|
|
public:
|
|
DeleteOnUnlockThread(QReadWriteLock **lock, QWaitCondition *startup, QMutex *waitMutex)
|
|
:m_lock(lock), m_startup(startup), m_waitMutex(waitMutex) {}
|
|
void run()
|
|
{
|
|
m_waitMutex->lock();
|
|
m_startup->wakeAll();
|
|
m_waitMutex->unlock();
|
|
|
|
// DeleteOnUnlockThread and the main thread will race from this point
|
|
(*m_lock)->lockForWrite();
|
|
(*m_lock)->unlock();
|
|
delete *m_lock;
|
|
}
|
|
private:
|
|
QReadWriteLock **m_lock;
|
|
QWaitCondition *m_startup;
|
|
QMutex *m_waitMutex;
|
|
};
|
|
|
|
void tst_QReadWriteLock::deleteOnUnlock()
|
|
{
|
|
QReadWriteLock *lock = 0;
|
|
QWaitCondition startup;
|
|
QMutex waitMutex;
|
|
|
|
DeleteOnUnlockThread thread2(&lock, &startup, &waitMutex);
|
|
|
|
QTime t;
|
|
t.start();
|
|
while(t.elapsed() < 4000) {
|
|
lock = new QReadWriteLock();
|
|
waitMutex.lock();
|
|
lock->lockForWrite();
|
|
thread2.start();
|
|
startup.wait(&waitMutex);
|
|
waitMutex.unlock();
|
|
|
|
// DeleteOnUnlockThread and the main thread will race from this point
|
|
lock->unlock();
|
|
|
|
thread2.wait();
|
|
}
|
|
}
|
|
|
|
|
|
void tst_QReadWriteLock::uncontendedLocks()
|
|
{
|
|
|
|
uint read=0;
|
|
uint write=0;
|
|
uint count=0;
|
|
int millisecs=1000;
|
|
{
|
|
QTime t;
|
|
t.start();
|
|
while(t.elapsed() <millisecs)
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
{
|
|
QReadWriteLock rwlock;
|
|
QTime t;
|
|
t.start();
|
|
while(t.elapsed() <millisecs)
|
|
{
|
|
rwlock.lockForRead();
|
|
rwlock.unlock();
|
|
++read;
|
|
}
|
|
}
|
|
{
|
|
QReadWriteLock rwlock;
|
|
QTime t;
|
|
t.start();
|
|
while(t.elapsed() <millisecs)
|
|
{
|
|
rwlock.lockForWrite();
|
|
rwlock.unlock();
|
|
++write;
|
|
}
|
|
}
|
|
|
|
qDebug("during %d millisecs:", millisecs);
|
|
qDebug("counted to %u", count);
|
|
qDebug("%u uncontended read locks/unlocks", read);
|
|
qDebug("%u uncontended write locks/unlocks", write);
|
|
}
|
|
|
|
enum { RecursiveLockCount = 10 };
|
|
|
|
void tst_QReadWriteLock::recursiveReadLock()
|
|
{
|
|
// thread to attempt locking for writing while the test recursively locks for reading
|
|
class RecursiveReadLockThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock *lock;
|
|
bool tryLockForWriteResult;
|
|
|
|
void run()
|
|
{
|
|
testsTurn.release();
|
|
|
|
// test is recursively locking for writing
|
|
for (int i = 0; i < RecursiveLockCount; ++i) {
|
|
threadsTurn.acquire();
|
|
tryLockForWriteResult = lock->tryLockForWrite();
|
|
testsTurn.release();
|
|
}
|
|
|
|
// test is releasing recursive write lock
|
|
for (int i = 0; i < RecursiveLockCount - 1; ++i) {
|
|
threadsTurn.acquire();
|
|
tryLockForWriteResult = lock->tryLockForWrite();
|
|
testsTurn.release();
|
|
}
|
|
|
|
// after final unlock in test, we should get the lock
|
|
threadsTurn.acquire();
|
|
tryLockForWriteResult = lock->tryLockForWrite();
|
|
testsTurn.release();
|
|
|
|
// cleanup
|
|
threadsTurn.acquire();
|
|
lock->unlock();
|
|
testsTurn.release();
|
|
|
|
// test will lockForRead(), then we will lockForWrite()
|
|
// (and block), purpose is to ensure that the test can
|
|
// recursive lockForRead() even with a waiting writer
|
|
threadsTurn.acquire();
|
|
// testsTurn.release(); // ### do not release here, the test uses tryAcquire()
|
|
lock->lockForWrite();
|
|
lock->unlock();
|
|
}
|
|
};
|
|
|
|
// init
|
|
QReadWriteLock lock(QReadWriteLock::Recursive);
|
|
RecursiveReadLockThread thread;
|
|
thread.lock = &lock;
|
|
thread.start();
|
|
|
|
testsTurn.acquire();
|
|
|
|
// verify that we can get multiple read locks in the same thread
|
|
for (int i = 0; i < RecursiveLockCount; ++i) {
|
|
QVERIFY(lock.tryLockForRead());
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
QVERIFY(!thread.tryLockForWriteResult);
|
|
}
|
|
|
|
// have to unlock the same number of times that we locked
|
|
for (int i = 0;i < RecursiveLockCount - 1; ++i) {
|
|
lock.unlock();
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
QVERIFY(!thread.tryLockForWriteResult);
|
|
}
|
|
|
|
// after the final unlock, we should be able to get the write lock
|
|
lock.unlock();
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
QVERIFY(thread.tryLockForWriteResult);
|
|
threadsTurn.release();
|
|
|
|
// check that recursive read locking works even when we have a waiting writer
|
|
testsTurn.acquire();
|
|
QVERIFY(lock.tryLockForRead());
|
|
threadsTurn.release();
|
|
|
|
testsTurn.tryAcquire(1, 1000);
|
|
QVERIFY(lock.tryLockForRead());
|
|
lock.unlock();
|
|
lock.unlock();
|
|
|
|
// cleanup
|
|
QVERIFY(thread.wait());
|
|
}
|
|
|
|
void tst_QReadWriteLock::recursiveWriteLock()
|
|
{
|
|
// thread to attempt locking for reading while the test recursively locks for writing
|
|
class RecursiveWriteLockThread : public QThread
|
|
{
|
|
public:
|
|
QReadWriteLock *lock;
|
|
bool tryLockForReadResult;
|
|
|
|
void run()
|
|
{
|
|
testsTurn.release();
|
|
|
|
// test is recursively locking for writing
|
|
for (int i = 0; i < RecursiveLockCount; ++i) {
|
|
threadsTurn.acquire();
|
|
tryLockForReadResult = lock->tryLockForRead();
|
|
testsTurn.release();
|
|
}
|
|
|
|
// test is releasing recursive write lock
|
|
for (int i = 0; i < RecursiveLockCount - 1; ++i) {
|
|
threadsTurn.acquire();
|
|
tryLockForReadResult = lock->tryLockForRead();
|
|
testsTurn.release();
|
|
}
|
|
|
|
// after final unlock in test, we should get the lock
|
|
threadsTurn.acquire();
|
|
tryLockForReadResult = lock->tryLockForRead();
|
|
testsTurn.release();
|
|
|
|
// cleanup
|
|
lock->unlock();
|
|
}
|
|
};
|
|
|
|
// init
|
|
QReadWriteLock lock(QReadWriteLock::Recursive);
|
|
RecursiveWriteLockThread thread;
|
|
thread.lock = &lock;
|
|
thread.start();
|
|
|
|
testsTurn.acquire();
|
|
|
|
// verify that we can get multiple read locks in the same thread
|
|
for (int i = 0; i < RecursiveLockCount; ++i) {
|
|
QVERIFY(lock.tryLockForWrite());
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
QVERIFY(!thread.tryLockForReadResult);
|
|
}
|
|
|
|
// have to unlock the same number of times that we locked
|
|
for (int i = 0;i < RecursiveLockCount - 1; ++i) {
|
|
lock.unlock();
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
QVERIFY(!thread.tryLockForReadResult);
|
|
}
|
|
|
|
// after the final unlock, thread should be able to get the read lock
|
|
lock.unlock();
|
|
threadsTurn.release();
|
|
|
|
testsTurn.acquire();
|
|
QVERIFY(thread.tryLockForReadResult);
|
|
|
|
// cleanup
|
|
QVERIFY(thread.wait());
|
|
}
|
|
|
|
QTEST_MAIN(tst_QReadWriteLock)
|
|
|
|
#include "tst_qreadwritelock.moc"
|