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 | |