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
59class tst_QReadWriteLock : public QObject
60{
61 Q_OBJECT
62
63/*
64 Singlethreaded tests
65*/
66private 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*/
81private 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*/
95private slots:
96 void uncontendedLocks();
97
98 // recursive locking tests
99 void recursiveReadLock();
100 void recursiveWriteLock();
101};
102
103void tst_QReadWriteLock::constructDestruct()
104{
105 {
106 QReadWriteLock rwlock;
107 }
108}
109
110void tst_QReadWriteLock::readLockUnlock()
111{
112 QReadWriteLock rwlock;
113 rwlock.lockForRead();
114 rwlock.unlock();
115}
116
117void tst_QReadWriteLock::writeLockUnlock()
118{
119 QReadWriteLock rwlock;
120 rwlock.lockForWrite();
121 rwlock.unlock();
122}
123
124void 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
135void 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
147void 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
160void 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
177void 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
191QAtomicInt lockCount(0);
192QReadWriteLock readWriteLock;
193QSemaphore testsTurn;
194QSemaphore threadsTurn;
195
196
197void 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
294void 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
410bool threadDone;
411QAtomicInt release;
412
413/*
414 write-lock
415 unlock
416 set threadone
417*/
418class WriteLockThread : public QThread
419{
420public:
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*/
436class ReadLockThread : public QThread
437{
438public:
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*/
453class WriteLockReleasableThread : public QThread
454{
455public:
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*/
473class ReadLockReleasableThread : public QThread
474{
475public:
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*/
496class ReadLockLoopThread : public QThread
497{
498public:
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*/
532class WriteLockLoopThread : public QThread
533{
534public:
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
561volatile 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*/
571class WriteLockCountThread : public QThread
572{
573public:
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*/
613class ReadLockCountThread : public QThread
614{
615public:
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*/
643void 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*/
660void 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*/
676void 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*/
700void 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*/
728void 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*/
750void 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*/
792void 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
829void 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*/
847class DeleteOnUnlockThread : public QThread
848{
849public:
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 }
863private:
864 QReadWriteLock **m_lock;
865 QWaitCondition *m_startup;
866 QMutex *m_waitMutex;
867};
868
869void 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
895void 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
939enum { RecursiveLockCount = 10 };
940
941void 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
1036void 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
1110QTEST_MAIN(tst_QReadWriteLock)
1111
1112#include "tst_qreadwritelock.moc"
1113

source code of qtbase/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp