tests/auto/qreadwritelock/tst_qreadwritelock.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the test suite of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 
       
    43 #include <QtTest/QtTest>
       
    44 #include <qcoreapplication.h>
       
    45 
       
    46 
       
    47 #include <qreadwritelock.h>
       
    48 #include <qmutex.h>
       
    49 #include <qthread.h>
       
    50 #include <qwaitcondition.h>
       
    51 
       
    52 #ifdef Q_OS_UNIX
       
    53 #include <unistd.h>
       
    54 #endif
       
    55 #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
       
    56 #include <windows.h>
       
    57 #define sleep(X) Sleep(X)
       
    58 #endif
       
    59 
       
    60 //on solaris, threads that loop one the release bool variable
       
    61 //needs to sleep more than 1 usec.
       
    62 #ifdef Q_OS_SOLARIS
       
    63 # define RWTESTSLEEP usleep(10);
       
    64 #else
       
    65 # define RWTESTSLEEP usleep(1);
       
    66 #endif
       
    67 
       
    68 #include <stdio.h>
       
    69 
       
    70 //TESTED_CLASS=
       
    71 //TESTED_FILES=
       
    72 
       
    73 class tst_QReadWriteLock : public QObject
       
    74 {
       
    75     Q_OBJECT
       
    76 public:
       
    77     tst_QReadWriteLock();
       
    78     virtual ~tst_QReadWriteLock();
       
    79 
       
    80 
       
    81 /*
       
    82     Singlethreaded tests
       
    83 */
       
    84 private slots:
       
    85 void constructDestruct();
       
    86 void readLockUnlock();
       
    87 void writeLockUnlock();
       
    88 void readLockUnlockLoop();
       
    89 void writeLockUnlockLoop();
       
    90 void readLockLoop();
       
    91 void writeLockLoop();
       
    92 void readWriteLockUnlockLoop();
       
    93 void tryReadLock();
       
    94 void tryWriteLock();
       
    95 /*
       
    96     Multithreaded tests
       
    97 */
       
    98 private slots:
       
    99 
       
   100 void readLockBlockRelease();
       
   101 void writeLockBlockRelease();
       
   102 void multipleReadersBlockRelease();
       
   103 void multipleReadersLoop();
       
   104 void multipleWritersLoop();
       
   105 void multipleReadersWritersLoop();
       
   106 void countingTest();
       
   107 void limitedReaders();
       
   108 void deleteOnUnlock();
       
   109 
       
   110 /*
       
   111     Performance tests
       
   112 */
       
   113 private slots:
       
   114 void uncontendedLocks();
       
   115 
       
   116     // recursive locking tests
       
   117     void recursiveReadLock();
       
   118     void recursiveWriteLock();
       
   119 };
       
   120 
       
   121 tst_QReadWriteLock::tst_QReadWriteLock()
       
   122 {
       
   123 
       
   124 }
       
   125 
       
   126 tst_QReadWriteLock::~tst_QReadWriteLock()
       
   127 {
       
   128 
       
   129 }
       
   130 
       
   131 void tst_QReadWriteLock::constructDestruct()
       
   132 {
       
   133     {
       
   134         QReadWriteLock rwlock;
       
   135     }
       
   136 }
       
   137 
       
   138 void tst_QReadWriteLock::readLockUnlock()
       
   139 {
       
   140      QReadWriteLock rwlock;
       
   141      rwlock.lockForRead();
       
   142      rwlock.unlock();
       
   143 }
       
   144 
       
   145 void tst_QReadWriteLock::writeLockUnlock()
       
   146 {
       
   147      QReadWriteLock rwlock;
       
   148      rwlock.lockForWrite();
       
   149      rwlock.unlock();
       
   150 }
       
   151 
       
   152 void tst_QReadWriteLock::readLockUnlockLoop()
       
   153 {
       
   154     QReadWriteLock rwlock;
       
   155     int runs=10000;
       
   156     int i;
       
   157     for (i=0; i<runs; ++i) {
       
   158         rwlock.lockForRead();
       
   159         rwlock.unlock();
       
   160     }
       
   161 }
       
   162 
       
   163 void tst_QReadWriteLock::writeLockUnlockLoop()
       
   164 {
       
   165     QReadWriteLock rwlock;
       
   166     int runs=10000;
       
   167     int i;
       
   168     for (i=0; i<runs; ++i) {
       
   169         rwlock.lockForWrite();
       
   170         rwlock.unlock();
       
   171     }
       
   172 }
       
   173 
       
   174 
       
   175 void tst_QReadWriteLock::readLockLoop()
       
   176 {
       
   177     QReadWriteLock rwlock;
       
   178     int runs=10000;
       
   179     int i;
       
   180     for (i=0; i<runs; ++i) {
       
   181         rwlock.lockForRead();
       
   182     }
       
   183     for (i=0; i<runs; ++i) {
       
   184         rwlock.unlock();
       
   185     }
       
   186 }
       
   187 
       
   188 void tst_QReadWriteLock::writeLockLoop()
       
   189 {
       
   190     /*
       
   191         If you include this, the test should print one line
       
   192         and then block.
       
   193     */
       
   194 #if 0
       
   195     QReadWriteLock rwlock;
       
   196     int runs=10000;
       
   197     int i;
       
   198     for (i=0; i<runs; ++i) {
       
   199         rwlock.lockForWrite();
       
   200         qDebug("I am going to block now.");
       
   201     }
       
   202 #endif
       
   203 }
       
   204 
       
   205 void tst_QReadWriteLock::readWriteLockUnlockLoop()
       
   206 {
       
   207     QReadWriteLock rwlock;
       
   208     int runs=10000;
       
   209     int i;
       
   210     for (i=0; i<runs; ++i) {
       
   211         rwlock.lockForRead();
       
   212         rwlock.unlock();
       
   213         rwlock.lockForWrite();
       
   214         rwlock.unlock();
       
   215     }
       
   216 
       
   217 }
       
   218 
       
   219 QAtomicInt lockCount(0);
       
   220 QReadWriteLock readWriteLock;
       
   221 QSemaphore testsTurn;
       
   222 QSemaphore threadsTurn;
       
   223 
       
   224 
       
   225 void tst_QReadWriteLock::tryReadLock()
       
   226 {
       
   227     QReadWriteLock rwlock;
       
   228     QVERIFY(rwlock.tryLockForRead());
       
   229     rwlock.unlock();
       
   230     QVERIFY(rwlock.tryLockForRead());
       
   231     rwlock.unlock();
       
   232 
       
   233     rwlock.lockForRead();
       
   234     rwlock.lockForRead();
       
   235     QVERIFY(rwlock.tryLockForRead());
       
   236     rwlock.unlock();
       
   237     rwlock.unlock();
       
   238     rwlock.unlock();
       
   239 
       
   240     rwlock.lockForWrite();
       
   241     QVERIFY(!rwlock.tryLockForRead());
       
   242     rwlock.unlock();
       
   243 
       
   244     // functionality test
       
   245     {
       
   246         class Thread : public QThread
       
   247         {
       
   248         public:
       
   249             void run()
       
   250             {
       
   251                 testsTurn.release();
       
   252 
       
   253                 threadsTurn.acquire();
       
   254                 QVERIFY(!readWriteLock.tryLockForRead());
       
   255                 testsTurn.release();
       
   256 
       
   257                 threadsTurn.acquire();
       
   258                 QVERIFY(readWriteLock.tryLockForRead());
       
   259                 lockCount.ref();
       
   260                 QVERIFY(readWriteLock.tryLockForRead());
       
   261                 lockCount.ref();
       
   262                 lockCount.deref();
       
   263                 readWriteLock.unlock();
       
   264                 lockCount.deref();
       
   265                 readWriteLock.unlock();
       
   266                 testsTurn.release();
       
   267 
       
   268                 threadsTurn.acquire();
       
   269                 QTime timer;
       
   270                 timer.start();
       
   271                 QVERIFY(!readWriteLock.tryLockForRead(1000));
       
   272                 QVERIFY(timer.elapsed() >= 1000);
       
   273                 testsTurn.release();
       
   274 
       
   275                 threadsTurn.acquire();
       
   276                 timer.start();
       
   277                 QVERIFY(readWriteLock.tryLockForRead(1000));
       
   278                 QVERIFY(timer.elapsed() <= 1000);
       
   279                 lockCount.ref();
       
   280                 QVERIFY(readWriteLock.tryLockForRead(1000));
       
   281                 lockCount.ref();
       
   282                 lockCount.deref();
       
   283                 readWriteLock.unlock();
       
   284                 lockCount.deref();
       
   285                 readWriteLock.unlock();
       
   286                 testsTurn.release();
       
   287 
       
   288                 threadsTurn.acquire();
       
   289             }
       
   290         };
       
   291 
       
   292         Thread thread;
       
   293         thread.start();
       
   294 
       
   295         testsTurn.acquire();
       
   296         readWriteLock.lockForWrite();
       
   297         QVERIFY(lockCount.testAndSetRelaxed(0, 1));
       
   298         threadsTurn.release();
       
   299 
       
   300         testsTurn.acquire();
       
   301         QVERIFY(lockCount.testAndSetRelaxed(1, 0));
       
   302         readWriteLock.unlock();
       
   303         threadsTurn.release();
       
   304 
       
   305         testsTurn.acquire();
       
   306         readWriteLock.lockForWrite();
       
   307         QVERIFY(lockCount.testAndSetRelaxed(0, 1));
       
   308         threadsTurn.release();
       
   309 
       
   310         testsTurn.acquire();
       
   311         QVERIFY(lockCount.testAndSetRelaxed(1, 0));
       
   312         readWriteLock.unlock();
       
   313         threadsTurn.release();
       
   314 
       
   315         // stop thread
       
   316         testsTurn.acquire();
       
   317         threadsTurn.release();
       
   318         thread.wait();
       
   319     }
       
   320 }
       
   321 
       
   322 void tst_QReadWriteLock::tryWriteLock()
       
   323 {
       
   324     {
       
   325         QReadWriteLock rwlock;
       
   326         QVERIFY(rwlock.tryLockForWrite());
       
   327         rwlock.unlock();
       
   328         QVERIFY(rwlock.tryLockForWrite());
       
   329         rwlock.unlock();
       
   330 
       
   331         rwlock.lockForWrite();
       
   332         QVERIFY(!rwlock.tryLockForWrite());
       
   333         QVERIFY(!rwlock.tryLockForWrite());
       
   334         rwlock.unlock();
       
   335 
       
   336         rwlock.lockForRead();
       
   337         QVERIFY(!rwlock.tryLockForWrite());
       
   338         rwlock.unlock();
       
   339     }
       
   340 
       
   341     {
       
   342         QReadWriteLock rwlock(QReadWriteLock::Recursive);
       
   343         QVERIFY(rwlock.tryLockForWrite());
       
   344         rwlock.unlock();
       
   345         QVERIFY(rwlock.tryLockForWrite());
       
   346         rwlock.unlock();
       
   347 
       
   348         rwlock.lockForWrite();
       
   349         QVERIFY(rwlock.tryLockForWrite());
       
   350         QVERIFY(rwlock.tryLockForWrite());
       
   351         rwlock.unlock();
       
   352         rwlock.unlock();
       
   353         rwlock.unlock();
       
   354 
       
   355         rwlock.lockForRead();
       
   356         QVERIFY(!rwlock.tryLockForWrite());
       
   357         rwlock.unlock();
       
   358     }
       
   359 
       
   360     // functionality test
       
   361     {
       
   362         class Thread : public QThread
       
   363         {
       
   364         public:
       
   365             void run()
       
   366             {
       
   367                 testsTurn.release();
       
   368 
       
   369                 threadsTurn.acquire();
       
   370                 Q_ASSERT(!readWriteLock.tryLockForWrite());
       
   371                 testsTurn.release();
       
   372 
       
   373                 threadsTurn.acquire();
       
   374                 Q_ASSERT(readWriteLock.tryLockForWrite());
       
   375                 Q_ASSERT(lockCount.testAndSetRelaxed(0, 1));
       
   376                 Q_ASSERT(lockCount.testAndSetRelaxed(1, 0));
       
   377                 readWriteLock.unlock();
       
   378                 testsTurn.release();
       
   379 
       
   380                 threadsTurn.acquire();
       
   381                 Q_ASSERT(!readWriteLock.tryLockForWrite(1000));
       
   382                 testsTurn.release();
       
   383 
       
   384                 threadsTurn.acquire();
       
   385                 Q_ASSERT(readWriteLock.tryLockForWrite(1000));
       
   386                 Q_ASSERT(lockCount.testAndSetRelaxed(0, 1));
       
   387                 Q_ASSERT(lockCount.testAndSetRelaxed(1, 0));
       
   388                 readWriteLock.unlock();
       
   389                 testsTurn.release();
       
   390 
       
   391                 threadsTurn.acquire();
       
   392             }
       
   393         };
       
   394 
       
   395         Thread thread;
       
   396         thread.start();
       
   397 
       
   398         testsTurn.acquire();
       
   399         readWriteLock.lockForRead();
       
   400         lockCount.ref();
       
   401         threadsTurn.release();
       
   402 
       
   403         testsTurn.acquire();
       
   404         lockCount.deref();
       
   405         readWriteLock.unlock();
       
   406         threadsTurn.release();
       
   407 
       
   408         testsTurn.acquire();
       
   409         readWriteLock.lockForRead();
       
   410         lockCount.ref();
       
   411         threadsTurn.release();
       
   412 
       
   413         testsTurn.acquire();
       
   414         lockCount.deref();
       
   415         readWriteLock.unlock();
       
   416         threadsTurn.release();
       
   417 
       
   418         // stop thread
       
   419         testsTurn.acquire();
       
   420         threadsTurn.release();
       
   421         thread.wait();
       
   422     }
       
   423 }
       
   424 
       
   425 bool threadDone;
       
   426 volatile bool release;
       
   427 
       
   428 /*
       
   429     write-lock
       
   430     unlock
       
   431     set threadone
       
   432 */
       
   433 class WriteLockThread : public QThread
       
   434 {
       
   435 public:
       
   436     QReadWriteLock &testRwlock;
       
   437     inline WriteLockThread(QReadWriteLock &l) : testRwlock(l) { }
       
   438     void run()
       
   439     {
       
   440         testRwlock.lockForWrite();
       
   441         testRwlock.unlock();
       
   442         threadDone=true;
       
   443     }
       
   444 };
       
   445 
       
   446 /*
       
   447     read-lock
       
   448     unlock
       
   449     set threadone
       
   450 */
       
   451 class ReadLockThread : public QThread
       
   452 {
       
   453 public:
       
   454     QReadWriteLock &testRwlock;
       
   455     inline ReadLockThread(QReadWriteLock &l) : testRwlock(l) { }
       
   456     void run()
       
   457     {
       
   458         testRwlock.lockForRead();
       
   459         testRwlock.unlock();
       
   460         threadDone=true;
       
   461     }
       
   462 };
       
   463 /*
       
   464     write-lock
       
   465     wait for release==true
       
   466     unlock
       
   467 */
       
   468 class WriteLockReleasableThread : public QThread
       
   469 {
       
   470 public:
       
   471     QReadWriteLock &testRwlock;
       
   472     inline WriteLockReleasableThread(QReadWriteLock &l) : testRwlock(l) { }
       
   473     void run()
       
   474     {
       
   475         testRwlock.lockForWrite();
       
   476         while(release==false) {
       
   477             RWTESTSLEEP
       
   478         }
       
   479         testRwlock.unlock();
       
   480     }
       
   481 };
       
   482 
       
   483 /*
       
   484     read-lock
       
   485     wait for release==true
       
   486     unlock
       
   487 */
       
   488 class ReadLockReleasableThread : public QThread
       
   489 {
       
   490 public:
       
   491     QReadWriteLock &testRwlock;
       
   492     inline ReadLockReleasableThread(QReadWriteLock &l) : testRwlock(l) { }
       
   493     void run()
       
   494     {
       
   495         testRwlock.lockForRead();
       
   496         while(release==false) {
       
   497             RWTESTSLEEP
       
   498         }
       
   499         testRwlock.unlock();
       
   500     }
       
   501 };
       
   502 
       
   503 
       
   504 /*
       
   505     for(runTime msecs)
       
   506         read-lock
       
   507         msleep(holdTime msecs)
       
   508         release lock
       
   509         msleep(waitTime msecs)
       
   510 */
       
   511 class ReadLockLoopThread : public QThread
       
   512 {
       
   513 public:
       
   514     QReadWriteLock &testRwlock;
       
   515     int runTime;
       
   516     int holdTime;
       
   517     int waitTime;
       
   518     bool print;
       
   519     QTime t;
       
   520     inline ReadLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false)
       
   521     :testRwlock(l)
       
   522     ,runTime(runTime)
       
   523     ,holdTime(holdTime)
       
   524     ,waitTime(waitTime)
       
   525     ,print(print)
       
   526     { }
       
   527     void run()
       
   528     {
       
   529         t.start();
       
   530         while (t.elapsed()<runTime)  {
       
   531             testRwlock.lockForRead();
       
   532             if(print) printf("reading\n");
       
   533             if (holdTime) msleep(holdTime);
       
   534             testRwlock.unlock();
       
   535             if (waitTime) msleep(waitTime);
       
   536         }
       
   537     }
       
   538 };
       
   539 
       
   540 /*
       
   541     for(runTime msecs)
       
   542         write-lock
       
   543         msleep(holdTime msecs)
       
   544         release lock
       
   545         msleep(waitTime msecs)
       
   546 */
       
   547 class WriteLockLoopThread : public QThread
       
   548 {
       
   549 public:
       
   550     QReadWriteLock &testRwlock;
       
   551     int runTime;
       
   552     int holdTime;
       
   553     int waitTime;
       
   554     bool print;
       
   555     QTime t;
       
   556     inline WriteLockLoopThread(QReadWriteLock &l, int runTime, int holdTime=0, int waitTime=0, bool print=false)
       
   557     :testRwlock(l)
       
   558     ,runTime(runTime)
       
   559     ,holdTime(holdTime)
       
   560     ,waitTime(waitTime)
       
   561     ,print(print)
       
   562     { }
       
   563     void run()
       
   564     {
       
   565         t.start();
       
   566         while (t.elapsed() < runTime)  {
       
   567             testRwlock.lockForWrite();
       
   568             if (print) printf(".");
       
   569             if (holdTime) msleep(holdTime);
       
   570             testRwlock.unlock();
       
   571             if (waitTime) msleep(waitTime);
       
   572         }
       
   573     }
       
   574 };
       
   575 
       
   576 volatile int count=0;
       
   577 
       
   578 /*
       
   579     for(runTime msecs)
       
   580         write-lock
       
   581         count to maxval
       
   582         set count to 0
       
   583         release lock
       
   584         msleep waitTime
       
   585 */
       
   586 class WriteLockCountThread : public QThread
       
   587 {
       
   588 public:
       
   589     QReadWriteLock &testRwlock;
       
   590     int runTime;
       
   591     int waitTime;
       
   592     int maxval;
       
   593     QTime t;
       
   594     inline WriteLockCountThread(QReadWriteLock &l, int runTime, int waitTime, int maxval)
       
   595     :testRwlock(l)
       
   596     ,runTime(runTime)
       
   597     ,waitTime(waitTime)
       
   598     ,maxval(maxval)
       
   599     { }
       
   600     void run()
       
   601     {
       
   602         t.start();
       
   603         while (t.elapsed() < runTime)  {
       
   604             testRwlock.lockForWrite();
       
   605             if(count)
       
   606                 qFatal("Non-zero count at start of write! (%d)",count );
       
   607 //            printf(".");
       
   608             int i;
       
   609             for(i=0; i<maxval; ++i) {
       
   610                 volatile int lc=count;
       
   611                 ++lc;
       
   612                 count=lc;
       
   613             }
       
   614             count=0;
       
   615             testRwlock.unlock();
       
   616             msleep(waitTime);
       
   617         }
       
   618     }
       
   619 };
       
   620 
       
   621 /*
       
   622     for(runTime msecs)
       
   623         read-lock
       
   624         verify count==0
       
   625         release lock
       
   626         msleep waitTime
       
   627 */
       
   628 class ReadLockCountThread : public QThread
       
   629 {
       
   630 public:
       
   631     QReadWriteLock &testRwlock;
       
   632     int runTime;
       
   633     int waitTime;
       
   634     QTime t;
       
   635     inline ReadLockCountThread(QReadWriteLock &l, int runTime, int waitTime)
       
   636     :testRwlock(l)
       
   637     ,runTime(runTime)
       
   638     ,waitTime(waitTime)
       
   639     { }
       
   640     void run()
       
   641     {
       
   642         t.start();
       
   643         while (t.elapsed() < runTime)  {
       
   644             testRwlock.lockForRead();
       
   645             if(count)
       
   646                 qFatal("Non-zero count at Read! (%d)",count );
       
   647             testRwlock.unlock();
       
   648             msleep(waitTime);
       
   649         }
       
   650     }
       
   651 };
       
   652 
       
   653 
       
   654 /*
       
   655     A writer aquires a read-lock, a reader locks
       
   656     the writer releases the lock, the reader gets the lock
       
   657 */
       
   658 void tst_QReadWriteLock::readLockBlockRelease()
       
   659 {
       
   660     QReadWriteLock testLock;
       
   661     testLock.lockForWrite();
       
   662     threadDone=false;
       
   663     ReadLockThread rlt(testLock);
       
   664     rlt.start();
       
   665     sleep(1);
       
   666     testLock.unlock();
       
   667     rlt.wait();
       
   668     QVERIFY(threadDone);
       
   669 }
       
   670 
       
   671 /*
       
   672     writer1 aquires a read-lock, writer2 blocks,
       
   673     writer1 releases the lock, writer2 gets the lock
       
   674 */
       
   675 void tst_QReadWriteLock::writeLockBlockRelease()
       
   676 {
       
   677     QReadWriteLock testLock;
       
   678     testLock.lockForWrite();
       
   679     threadDone=false;
       
   680     WriteLockThread wlt(testLock);
       
   681     wlt.start();
       
   682     sleep(1);
       
   683     testLock.unlock();
       
   684     wlt.wait();
       
   685     QVERIFY(threadDone);
       
   686 }
       
   687 /*
       
   688     Two readers aquire a read-lock, one writer attempts a write block,
       
   689     the readers release their locks, the writer gets the lock.
       
   690 */
       
   691 void tst_QReadWriteLock::multipleReadersBlockRelease()
       
   692 {
       
   693 
       
   694     QReadWriteLock testLock;
       
   695     release=false;
       
   696     threadDone=false;
       
   697     ReadLockReleasableThread rlt1(testLock);
       
   698     ReadLockReleasableThread rlt2(testLock);
       
   699     rlt1.start();
       
   700     rlt2.start();
       
   701     sleep(1);
       
   702     WriteLockThread wlt(testLock);
       
   703     wlt.start();
       
   704     sleep(1);
       
   705     release=true;
       
   706     wlt.wait();
       
   707     rlt1.wait();
       
   708     rlt2.wait();
       
   709     QVERIFY(threadDone);
       
   710 }
       
   711 
       
   712 /*
       
   713     Multiple readers locks and unlocks a lock.
       
   714 */
       
   715 void tst_QReadWriteLock::multipleReadersLoop()
       
   716 {
       
   717     int time=500;
       
   718     int hold=250;
       
   719     int wait=0;
       
   720 #if defined (Q_OS_HPUX)
       
   721     const int numthreads=50;
       
   722 #elif defined(Q_OS_VXWORKS)
       
   723     const int numthreads=40;
       
   724 #else
       
   725     const int numthreads=75;
       
   726 #endif
       
   727     QReadWriteLock testLock;
       
   728     ReadLockLoopThread *threads[numthreads];
       
   729     int i;
       
   730     for (i=0; i<numthreads; ++i)
       
   731         threads[i] = new ReadLockLoopThread(testLock, time, hold, wait);
       
   732     for (i=0; i<numthreads; ++i)
       
   733         threads[i]->start();
       
   734     for (i=0; i<numthreads; ++i)
       
   735         threads[i]->wait();
       
   736     for (i=0; i<numthreads; ++i)
       
   737         delete threads[i];
       
   738 }
       
   739 
       
   740 /*
       
   741     Multiple writers locks and unlocks a lock.
       
   742 */
       
   743 void tst_QReadWriteLock::multipleWritersLoop()
       
   744 {
       
   745         int time=500;
       
   746         int wait=0;
       
   747         int hold=0;
       
   748         const int numthreads=50;
       
   749         QReadWriteLock testLock;
       
   750         WriteLockLoopThread *threads[numthreads];
       
   751         int i;
       
   752         for (i=0; i<numthreads; ++i)
       
   753             threads[i] = new WriteLockLoopThread(testLock, time, hold, wait);
       
   754         for (i=0; i<numthreads; ++i)
       
   755             threads[i]->start();
       
   756         for (i=0; i<numthreads; ++i)
       
   757             threads[i]->wait();
       
   758         for (i=0; i<numthreads; ++i)
       
   759             delete threads[i];
       
   760 }
       
   761 
       
   762 /*
       
   763     Multiple readers and writers locks and unlocks a lock.
       
   764 */
       
   765 void tst_QReadWriteLock::multipleReadersWritersLoop()
       
   766 {
       
   767         //int time=INT_MAX;
       
   768         int time=10000;
       
   769         int readerThreads=20;
       
   770         int readerWait=0;
       
   771         int readerHold=1;
       
   772 
       
   773         int writerThreads=2;
       
   774         int writerWait=500;
       
   775         int writerHold=50;
       
   776 
       
   777         QReadWriteLock testLock;
       
   778         ReadLockLoopThread  *readers[1024];
       
   779         WriteLockLoopThread *writers[1024];
       
   780         int i;
       
   781 
       
   782         for (i=0; i<readerThreads; ++i)
       
   783             readers[i] = new ReadLockLoopThread(testLock, time, readerHold, readerWait, false);
       
   784         for (i=0; i<writerThreads; ++i)
       
   785             writers[i] = new WriteLockLoopThread(testLock, time, writerHold, writerWait, false);
       
   786 
       
   787         for (i=0; i<readerThreads; ++i)
       
   788             readers[i]->start(QThread::NormalPriority);
       
   789         for (i=0; i<writerThreads; ++i)
       
   790             writers[i]->start(QThread::IdlePriority);
       
   791 
       
   792         for (i=0; i<readerThreads; ++i)
       
   793             readers[i]->wait();
       
   794         for (i=0; i<writerThreads; ++i)
       
   795             writers[i]->wait();
       
   796 
       
   797         for (i=0; i<readerThreads; ++i)
       
   798             delete readers[i];
       
   799         for (i=0; i<writerThreads; ++i)
       
   800             delete writers[i];
       
   801 }
       
   802 
       
   803 /*
       
   804     Writers increment a variable from 0 to maxval, then reset it to 0.
       
   805     Readers verify that the variable remains at 0.
       
   806 */
       
   807 void tst_QReadWriteLock::countingTest()
       
   808 {
       
   809         //int time=INT_MAX;
       
   810         int time=10000;
       
   811         int readerThreads=20;
       
   812         int readerWait=1;
       
   813 
       
   814         int writerThreads=3;
       
   815         int writerWait=150;
       
   816         int maxval=10000;
       
   817 
       
   818         QReadWriteLock testLock;
       
   819         ReadLockCountThread  *readers[1024];
       
   820         WriteLockCountThread *writers[1024];
       
   821         int i;
       
   822 
       
   823         for (i=0; i<readerThreads; ++i)
       
   824             readers[i] = new ReadLockCountThread(testLock, time,  readerWait);
       
   825         for (i=0; i<writerThreads; ++i)
       
   826             writers[i] = new WriteLockCountThread(testLock, time,  writerWait, maxval);
       
   827 
       
   828         for (i=0; i<readerThreads; ++i)
       
   829             readers[i]->start(QThread::NormalPriority);
       
   830         for (i=0; i<writerThreads; ++i)
       
   831             writers[i]->start(QThread::LowestPriority);
       
   832 
       
   833         for (i=0; i<readerThreads; ++i)
       
   834             readers[i]->wait();
       
   835         for (i=0; i<writerThreads; ++i)
       
   836             writers[i]->wait();
       
   837 
       
   838         for (i=0; i<readerThreads; ++i)
       
   839             delete readers[i];
       
   840         for (i=0; i<writerThreads; ++i)
       
   841             delete writers[i];
       
   842 }
       
   843 
       
   844 void tst_QReadWriteLock::limitedReaders()
       
   845 {
       
   846 
       
   847 };
       
   848 
       
   849 /*
       
   850     Test a race-condition that may happen if one thread is in unlock() while
       
   851     another thread deletes the rw-lock.
       
   852 
       
   853     MainThread              DeleteOnUnlockThread
       
   854 
       
   855     write-lock
       
   856     unlock
       
   857       |                     write-lock
       
   858       |                     unlock
       
   859       |                     delete lock
       
   860     deref d inside unlock
       
   861 */
       
   862 class DeleteOnUnlockThread : public QThread
       
   863 {
       
   864 public:
       
   865     DeleteOnUnlockThread(QReadWriteLock **lock, QWaitCondition *startup, QMutex *waitMutex)
       
   866     :m_lock(lock), m_startup(startup), m_waitMutex(waitMutex) {}
       
   867     void run()
       
   868     {
       
   869         m_waitMutex->lock();
       
   870         m_startup->wakeAll();
       
   871         m_waitMutex->unlock();
       
   872 
       
   873         // DeleteOnUnlockThread and the main thread will race from this point
       
   874         (*m_lock)->lockForWrite();
       
   875         (*m_lock)->unlock();
       
   876         delete *m_lock;
       
   877     }
       
   878 private:
       
   879     QReadWriteLock **m_lock;
       
   880     QWaitCondition *m_startup;
       
   881     QMutex *m_waitMutex;
       
   882 };
       
   883 
       
   884 void tst_QReadWriteLock::deleteOnUnlock()
       
   885 {
       
   886     QReadWriteLock *lock = 0;
       
   887     QWaitCondition startup;
       
   888     QMutex waitMutex;
       
   889 
       
   890     DeleteOnUnlockThread thread2(&lock, &startup, &waitMutex);
       
   891 
       
   892     QTime t;
       
   893     t.start();
       
   894     while(t.elapsed() < 4000) {
       
   895         lock = new QReadWriteLock();
       
   896         waitMutex.lock();
       
   897         lock->lockForWrite();
       
   898         thread2.start();
       
   899         startup.wait(&waitMutex);
       
   900         waitMutex.unlock();
       
   901 
       
   902         // DeleteOnUnlockThread and the main thread will race from this point
       
   903         lock->unlock();
       
   904 
       
   905         thread2.wait();
       
   906     }
       
   907 }
       
   908 
       
   909 
       
   910 void tst_QReadWriteLock::uncontendedLocks()
       
   911 {
       
   912 
       
   913     uint read=0;
       
   914     uint write=0;
       
   915     uint count=0;
       
   916     int millisecs=1000;
       
   917     {
       
   918         QTime t;
       
   919         t.start();
       
   920         while(t.elapsed() <millisecs)
       
   921         {
       
   922             ++count;
       
   923         }
       
   924     }
       
   925     {
       
   926         QReadWriteLock rwlock;
       
   927         QTime t;
       
   928         t.start();
       
   929         while(t.elapsed() <millisecs)
       
   930         {
       
   931             rwlock.lockForRead();
       
   932             rwlock.unlock();
       
   933             ++read;
       
   934         }
       
   935     }
       
   936     {
       
   937         QReadWriteLock rwlock;
       
   938         QTime t;
       
   939         t.start();
       
   940         while(t.elapsed() <millisecs)
       
   941         {
       
   942             rwlock.lockForWrite();
       
   943             rwlock.unlock();
       
   944             ++write;
       
   945         }
       
   946     }
       
   947 
       
   948     qDebug("during %d millisecs:", millisecs);
       
   949     qDebug("counted to %u", count);
       
   950     qDebug("%u uncontended read locks/unlocks", read);
       
   951     qDebug("%u uncontended write locks/unlocks", write);
       
   952 }
       
   953 
       
   954 enum { RecursiveLockCount = 10 };
       
   955 
       
   956 void tst_QReadWriteLock::recursiveReadLock()
       
   957 {
       
   958     // thread to attempt locking for writing while the test recursively locks for reading
       
   959     class RecursiveReadLockThread : public QThread
       
   960     {
       
   961     public:
       
   962         QReadWriteLock *lock;
       
   963         bool tryLockForWriteResult;
       
   964 
       
   965         void run()
       
   966         {
       
   967             testsTurn.release();
       
   968 
       
   969             // test is recursively locking for writing
       
   970             for (int i = 0; i < RecursiveLockCount; ++i) {
       
   971                 threadsTurn.acquire();
       
   972                 tryLockForWriteResult = lock->tryLockForWrite();
       
   973                 testsTurn.release();
       
   974             }
       
   975 
       
   976             // test is releasing recursive write lock
       
   977             for (int i = 0; i < RecursiveLockCount - 1; ++i) {
       
   978                 threadsTurn.acquire();
       
   979                 tryLockForWriteResult = lock->tryLockForWrite();
       
   980                 testsTurn.release();
       
   981             }
       
   982 
       
   983             // after final unlock in test, we should get the lock
       
   984             threadsTurn.acquire();
       
   985             tryLockForWriteResult = lock->tryLockForWrite();
       
   986             testsTurn.release();
       
   987 
       
   988             // cleanup
       
   989             threadsTurn.acquire();
       
   990             lock->unlock();
       
   991             testsTurn.release();
       
   992 
       
   993             // test will lockForRead(), then we will lockForWrite()
       
   994             // (and block), purpose is to ensure that the test can
       
   995             // recursive lockForRead() even with a waiting writer
       
   996             threadsTurn.acquire();
       
   997             // testsTurn.release(); // ### do not release here, the test uses tryAcquire()
       
   998             lock->lockForWrite();
       
   999             lock->unlock();
       
  1000         }
       
  1001     };
       
  1002 
       
  1003     // init
       
  1004     QReadWriteLock lock(QReadWriteLock::Recursive);
       
  1005     RecursiveReadLockThread thread;
       
  1006     thread.lock = &lock;
       
  1007     thread.start();
       
  1008 
       
  1009     testsTurn.acquire();
       
  1010 
       
  1011     // verify that we can get multiple read locks in the same thread
       
  1012     for (int i = 0; i < RecursiveLockCount; ++i) {
       
  1013         QVERIFY(lock.tryLockForRead());
       
  1014         threadsTurn.release();
       
  1015 
       
  1016         testsTurn.acquire();
       
  1017         QVERIFY(!thread.tryLockForWriteResult);
       
  1018     }
       
  1019 
       
  1020     // have to unlock the same number of times that we locked
       
  1021     for (int i = 0;i < RecursiveLockCount - 1; ++i) {
       
  1022         lock.unlock();
       
  1023         threadsTurn.release();
       
  1024 
       
  1025         testsTurn.acquire();
       
  1026         QVERIFY(!thread.tryLockForWriteResult);
       
  1027     }
       
  1028 
       
  1029     // after the final unlock, we should be able to get the write lock
       
  1030     lock.unlock();
       
  1031     threadsTurn.release();
       
  1032 
       
  1033     testsTurn.acquire();
       
  1034     QVERIFY(thread.tryLockForWriteResult);
       
  1035     threadsTurn.release();
       
  1036 
       
  1037     // check that recursive read locking works even when we have a waiting writer
       
  1038     testsTurn.acquire();
       
  1039     QVERIFY(lock.tryLockForRead());
       
  1040     threadsTurn.release();
       
  1041 
       
  1042     testsTurn.tryAcquire(1, 1000);
       
  1043     QVERIFY(lock.tryLockForRead());
       
  1044     lock.unlock();
       
  1045     lock.unlock();
       
  1046 
       
  1047     // cleanup
       
  1048     QVERIFY(thread.wait());
       
  1049 }
       
  1050 
       
  1051 void tst_QReadWriteLock::recursiveWriteLock()
       
  1052 {
       
  1053     // thread to attempt locking for reading while the test recursively locks for writing
       
  1054     class RecursiveWriteLockThread : public QThread
       
  1055     {
       
  1056     public:
       
  1057         QReadWriteLock *lock;
       
  1058         bool tryLockForReadResult;
       
  1059 
       
  1060         void run()
       
  1061         {
       
  1062             testsTurn.release();
       
  1063 
       
  1064             // test is recursively locking for writing
       
  1065             for (int i = 0; i < RecursiveLockCount; ++i) {
       
  1066                 threadsTurn.acquire();
       
  1067                 tryLockForReadResult = lock->tryLockForRead();
       
  1068                 testsTurn.release();
       
  1069             }
       
  1070 
       
  1071             // test is releasing recursive write lock
       
  1072             for (int i = 0; i < RecursiveLockCount - 1; ++i) {
       
  1073                 threadsTurn.acquire();
       
  1074                 tryLockForReadResult = lock->tryLockForRead();
       
  1075                 testsTurn.release();
       
  1076             }
       
  1077 
       
  1078             // after final unlock in test, we should get the lock
       
  1079             threadsTurn.acquire();
       
  1080             tryLockForReadResult = lock->tryLockForRead();
       
  1081             testsTurn.release();
       
  1082 
       
  1083             // cleanup
       
  1084             lock->unlock();
       
  1085         }
       
  1086     };
       
  1087 
       
  1088     // init
       
  1089     QReadWriteLock lock(QReadWriteLock::Recursive);
       
  1090     RecursiveWriteLockThread thread;
       
  1091     thread.lock = &lock;
       
  1092     thread.start();
       
  1093 
       
  1094     testsTurn.acquire();
       
  1095 
       
  1096     // verify that we can get multiple read locks in the same thread
       
  1097     for (int i = 0; i < RecursiveLockCount; ++i) {
       
  1098         QVERIFY(lock.tryLockForWrite());
       
  1099         threadsTurn.release();
       
  1100 
       
  1101         testsTurn.acquire();
       
  1102         QVERIFY(!thread.tryLockForReadResult);
       
  1103     }
       
  1104 
       
  1105     // have to unlock the same number of times that we locked
       
  1106     for (int i = 0;i < RecursiveLockCount - 1; ++i) {
       
  1107         lock.unlock();
       
  1108         threadsTurn.release();
       
  1109 
       
  1110         testsTurn.acquire();
       
  1111         QVERIFY(!thread.tryLockForReadResult);
       
  1112     }
       
  1113 
       
  1114     // after the final unlock, thread should be able to get the read lock
       
  1115     lock.unlock();
       
  1116     threadsTurn.release();
       
  1117 
       
  1118     testsTurn.acquire();
       
  1119     QVERIFY(thread.tryLockForReadResult);
       
  1120 
       
  1121     // cleanup
       
  1122     QVERIFY(thread.wait());
       
  1123 }
       
  1124 
       
  1125 QTEST_MAIN(tst_QReadWriteLock)
       
  1126 
       
  1127 #include "tst_qreadwritelock.moc"