summaryrefslogtreecommitdiffstats
path: root/tests/auto/qreadwritelock
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/qreadwritelock')
-rw-r--r--tests/auto/qreadwritelock/.gitignore1
-rw-r--r--tests/auto/qreadwritelock/qreadwritelock.pro4
-rw-r--r--tests/auto/qreadwritelock/tst_qreadwritelock.cpp1127
3 files changed, 1132 insertions, 0 deletions
diff --git a/tests/auto/qreadwritelock/.gitignore b/tests/auto/qreadwritelock/.gitignore
new file mode 100644
index 0000000000..96fda685f5
--- /dev/null
+++ b/tests/auto/qreadwritelock/.gitignore
@@ -0,0 +1 @@
+tst_qreadwritelock
diff --git a/tests/auto/qreadwritelock/qreadwritelock.pro b/tests/auto/qreadwritelock/qreadwritelock.pro
new file mode 100644
index 0000000000..93f7c68dc3
--- /dev/null
+++ b/tests/auto/qreadwritelock/qreadwritelock.pro
@@ -0,0 +1,4 @@
+load(qttest_p4)
+SOURCES += tst_qreadwritelock.cpp
+QT = core
+CONFIG += parallel_test
diff --git a/tests/auto/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/qreadwritelock/tst_qreadwritelock.cpp
new file mode 100644
index 0000000000..e0cc2fa200
--- /dev/null
+++ b/tests/auto/qreadwritelock/tst_qreadwritelock.cpp
@@ -0,0 +1,1127 @@
+/****************************************************************************
+**
+** 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"