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