/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Copyright (C) 2016 Ivailo Monev ** ** This file is part of the test suite of the Katie Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** ** GNU Lesser General Public License Usage ** 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. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include //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 //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(); }; 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= 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(); } // 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; 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()start(); for (i=0; iwait(); for (i=0; istart(); for (i=0; iwait(); for (i=0; istart(QThread::NormalPriority); for (i=0; istart(QThread::IdlePriority); for (i=0; iwait(); for (i=0; iwait(); for (i=0; istart(QThread::NormalPriority); for (i=0; istart(QThread::LowestPriority); for (i=0; iwait(); for (i=0; iwait(); for (i=0; ilock(); 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()