blob: 47bae585a1a5a741e6587902a49df399dd29a536 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qcoreapplication.h>
#include <qreadwritelock.h>
#include <qelapsedtimer.h>
#include <qmutex.h>
#include <qthread.h>
#include <qwaitcondition.h>
#ifdef Q_OS_UNIX
#include <unistd.h>
#endif
#if defined(Q_OS_WIN)
# include <qt_windows.h>
# ifndef Q_OS_WINRT
# define sleep(X) Sleep(X)
# else
# define sleep(X) WaitForSingleObjectEx(GetCurrentThread(), X, FALSE);
# endif
#endif
//on solaris, threads that loop on 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>
class tst_QReadWriteLock : public QObject
{
Q_OBJECT
/*
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();
};
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();
QElapsedTimer 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:
Thread() : failureCount(0) { }
void run()
{
testsTurn.release();
threadsTurn.acquire();
if (readWriteLock.tryLockForWrite())
failureCount++;
testsTurn.release();
threadsTurn.acquire();
if (!readWriteLock.tryLockForWrite())
failureCount++;
if (!lockCount.testAndSetRelaxed(0, 1))
failureCount++;
if (!lockCount.testAndSetRelaxed(1, 0))
failureCount++;
readWriteLock.unlock();
testsTurn.release();
threadsTurn.acquire();
if (readWriteLock.tryLockForWrite(1000))
failureCount++;
testsTurn.release();
threadsTurn.acquire();
if (!readWriteLock.tryLockForWrite(1000))
failureCount++;
if (!lockCount.testAndSetRelaxed(0, 1))
failureCount++;
if (!lockCount.testAndSetRelaxed(1, 0))
failureCount++;
readWriteLock.unlock();
testsTurn.release();
threadsTurn.acquire();
}
int failureCount;
};
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();
QCOMPARE(thread.failureCount, 0);
}
}
bool threadDone;
QAtomicInt 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.loadRelaxed() == 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.loadRelaxed() == 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;
QElapsedTimer 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;
QElapsedTimer 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;
QElapsedTimer 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;
QElapsedTimer 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 acquires 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 acquires 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 acquire 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.storeRelaxed(false);
threadDone=false;
ReadLockReleasableThread rlt1(testLock);
ReadLockReleasableThread rlt2(testLock);
rlt1.start();
rlt2.start();
sleep(1);
WriteLockThread wlt(testLock);
wlt.start();
sleep(1);
release.storeRelaxed(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);
QElapsedTimer 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;
{
QElapsedTimer t;
t.start();
while(t.elapsed() <millisecs)
{
++count;
}
}
{
QReadWriteLock rwlock;
QElapsedTimer t;
t.start();
while(t.elapsed() <millisecs)
{
rwlock.lockForRead();
rwlock.unlock();
++read;
}
}
{
QReadWriteLock rwlock;
QElapsedTimer 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"