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