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
31#include <qatomic.h>
32#include <qcoreapplication.h>
33#include <qmutex.h>
34#include <qthread.h>
35#include <qwaitcondition.h>
36
37#define COND_WAIT_TIME 1
38
39class tst_QWaitCondition : public QObject
40{
41 Q_OBJECT
42private slots:
43 void wait_QMutex();
44 void wait_QReadWriteLock();
45 void wakeOne();
46 void wakeAll();
47 void wait_RaceCondition();
48};
49
50static const int iterations = 4;
51static const int ThreadCount = 4;
52
53// Terminate thread in destructor for threads instantiated on the stack
54class TerminatingThread : public QThread
55{
56public:
57 explicit TerminatingThread()
58 {
59 setTerminationEnabled(true);
60 }
61
62 ~TerminatingThread()
63 {
64 if (isRunning()) {
65 qWarning() << "forcibly terminating " << objectName();
66 terminate();
67 }
68 }
69};
70
71class wait_QMutex_Thread_1 : public TerminatingThread
72{
73public:
74 QMutex mutex;
75 QWaitCondition cond;
76
77 inline wait_QMutex_Thread_1()
78 { }
79
80 void run()
81 {
82 mutex.lock();
83 cond.wakeOne();
84 cond.wait(lockedMutex: &mutex);
85 mutex.unlock();
86 }
87};
88
89class wait_QMutex_Thread_2 : public TerminatingThread
90{
91public:
92 QWaitCondition started;
93
94 QMutex *mutex;
95 QWaitCondition *cond;
96
97 inline wait_QMutex_Thread_2()
98 : mutex(0), cond(0)
99 { }
100
101 void run()
102 {
103 mutex->lock();
104 started.wakeOne();
105 cond->wait(lockedMutex: mutex);
106 mutex->unlock();
107 }
108};
109
110class wait_QReadWriteLock_Thread_1 : public TerminatingThread
111{
112public:
113 QReadWriteLock readWriteLock;
114 QWaitCondition cond;
115
116 inline wait_QReadWriteLock_Thread_1()
117 { }
118
119 void run()
120 {
121 readWriteLock.lockForWrite();
122 cond.wakeOne();
123 cond.wait(lockedReadWriteLock: &readWriteLock);
124 readWriteLock.unlock();
125 }
126};
127
128class wait_QReadWriteLock_Thread_2 : public TerminatingThread
129{
130public:
131 QWaitCondition started;
132
133 QReadWriteLock *readWriteLock;
134 QWaitCondition *cond;
135
136 inline wait_QReadWriteLock_Thread_2()
137 : readWriteLock(0), cond(0)
138 { }
139
140 void run()
141 {
142 readWriteLock->lockForRead();
143 started.wakeOne();
144 cond->wait(lockedReadWriteLock: readWriteLock);
145 readWriteLock->unlock();
146 }
147};
148
149void tst_QWaitCondition::wait_QMutex()
150{
151 int x;
152 for (int i = 0; i < iterations; ++i) {
153 {
154 QMutex mutex;
155 QWaitCondition cond;
156
157 mutex.lock();
158
159 cond.wakeOne();
160 QVERIFY(!cond.wait(&mutex, 1));
161
162 cond.wakeAll();
163 QVERIFY(!cond.wait(&mutex, 1));
164
165 mutex.unlock();
166 }
167
168 {
169 // test multiple threads waiting on separate wait conditions
170 wait_QMutex_Thread_1 thread[ThreadCount];
171
172 const QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex_")
173 + QString::number(i) + QLatin1Char('_');
174
175 for (x = 0; x < ThreadCount; ++x) {
176 thread[x].setObjectName(prefix + QString::number(x));
177 thread[x].mutex.lock();
178 thread[x].start();
179 // wait for thread to start
180 QVERIFY(thread[x].cond.wait(&thread[x].mutex, 1000));
181 thread[x].mutex.unlock();
182 }
183
184 for (x = 0; x < ThreadCount; ++x) {
185 QVERIFY(thread[x].isRunning());
186 QVERIFY(!thread[x].isFinished());
187 }
188
189 for (x = 0; x < ThreadCount; ++x) {
190 thread[x].mutex.lock();
191 thread[x].cond.wakeOne();
192 thread[x].mutex.unlock();
193 }
194
195 for (x = 0; x < ThreadCount; ++x) {
196 QVERIFY(thread[x].wait(1000));
197 }
198 }
199
200 {
201 // test multiple threads waiting on a wait condition
202 QMutex mutex;
203 QWaitCondition cond1, cond2;
204 wait_QMutex_Thread_2 thread[ThreadCount];
205
206 const QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex_")
207 + QString::number(i) + QLatin1Char('_');
208
209 mutex.lock();
210 for (x = 0; x < ThreadCount; ++x) {
211 thread[x].setObjectName(prefix + QString::number(x));
212 thread[x].mutex = &mutex;
213 thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2;
214 thread[x].start();
215 // wait for thread to start
216 QVERIFY(thread[x].started.wait(&mutex, 1000));
217 }
218 mutex.unlock();
219
220 for (x = 0; x < ThreadCount; ++x) {
221 QVERIFY(thread[x].isRunning());
222 QVERIFY(!thread[x].isFinished());
223 }
224
225 mutex.lock();
226 cond1.wakeAll();
227 cond2.wakeAll();
228 mutex.unlock();
229
230 for (x = 0; x < ThreadCount; ++x) {
231 QVERIFY(thread[x].wait(1000));
232 }
233 }
234 }
235}
236
237void tst_QWaitCondition::wait_QReadWriteLock()
238{
239 {
240 QReadWriteLock readWriteLock(QReadWriteLock::Recursive);
241 QWaitCondition waitCondition;
242
243 // ensure that the lockForRead is correctly restored
244 readWriteLock.lockForRead();
245
246 QVERIFY(!waitCondition.wait(&readWriteLock, 1));
247
248 QVERIFY(!readWriteLock.tryLockForWrite());
249 QVERIFY(readWriteLock.tryLockForRead());
250 readWriteLock.unlock();
251 QVERIFY(!readWriteLock.tryLockForWrite());
252 readWriteLock.unlock();
253
254 QVERIFY(readWriteLock.tryLockForWrite());
255 readWriteLock.unlock();
256 }
257
258 {
259 QReadWriteLock readWriteLock(QReadWriteLock::Recursive);
260 QWaitCondition waitCondition;
261
262 // ensure that the lockForWrite is correctly restored
263 readWriteLock.lockForWrite();
264
265 QVERIFY(!waitCondition.wait(&readWriteLock, 1));
266
267 QVERIFY(!readWriteLock.tryLockForRead());
268 QVERIFY(readWriteLock.tryLockForWrite());
269 readWriteLock.unlock();
270 QVERIFY(!readWriteLock.tryLockForRead());
271 readWriteLock.unlock();
272
273 QVERIFY(readWriteLock.tryLockForRead());
274 readWriteLock.unlock();
275 }
276
277
278 int x;
279 for (int i = 0; i < iterations; ++i) {
280 {
281 QReadWriteLock readWriteLock;
282 QWaitCondition waitCondition;
283
284 readWriteLock.lockForRead();
285
286 waitCondition.wakeOne();
287 QVERIFY(!waitCondition.wait(&readWriteLock, 1));
288
289 waitCondition.wakeAll();
290 QVERIFY(!waitCondition.wait(&readWriteLock, 1));
291
292 readWriteLock.unlock();
293 }
294
295 {
296 QReadWriteLock readWriteLock;
297 QWaitCondition waitCondition;
298
299 readWriteLock.lockForWrite();
300
301 waitCondition.wakeOne();
302 QVERIFY(!waitCondition.wait(&readWriteLock, 1));
303
304 waitCondition.wakeAll();
305 QVERIFY(!waitCondition.wait(&readWriteLock, 1));
306
307 readWriteLock.unlock();
308 }
309
310 {
311 // test multiple threads waiting on separate wait conditions
312 wait_QReadWriteLock_Thread_1 thread[ThreadCount];
313
314 const QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_lockforread_");
315
316 for (x = 0; x < ThreadCount; ++x) {
317 thread[x].setObjectName(prefix + QString::number(x));
318 thread[x].readWriteLock.lockForRead();
319 thread[x].start();
320 // wait for thread to start
321 QVERIFY(thread[x].cond.wait(&thread[x].readWriteLock, 1000));
322 thread[x].readWriteLock.unlock();
323 }
324
325 for (x = 0; x < ThreadCount; ++x) {
326 QVERIFY(thread[x].isRunning());
327 QVERIFY(!thread[x].isFinished());
328 }
329
330 for (x = 0; x < ThreadCount; ++x) {
331 thread[x].readWriteLock.lockForRead();
332 thread[x].cond.wakeOne();
333 thread[x].readWriteLock.unlock();
334 }
335
336 for (x = 0; x < ThreadCount; ++x) {
337 QVERIFY(thread[x].wait(1000));
338 }
339 }
340
341 {
342 // test multiple threads waiting on a wait condition
343 QReadWriteLock readWriteLock;
344 QWaitCondition cond1, cond2;
345 wait_QReadWriteLock_Thread_2 thread[ThreadCount];
346
347 const QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_lockforwrite_");
348
349 readWriteLock.lockForWrite();
350 for (x = 0; x < ThreadCount; ++x) {
351 thread[x].setObjectName(prefix + QString::number(x));
352 thread[x].readWriteLock = &readWriteLock;
353 thread[x].cond = (x < ThreadCount / 2) ? &cond1 : &cond2;
354 thread[x].start();
355 // wait for thread to start
356 QVERIFY(thread[x].started.wait(&readWriteLock, 1000));
357 }
358 readWriteLock.unlock();
359
360 for (x = 0; x < ThreadCount; ++x) {
361 QVERIFY(thread[x].isRunning());
362 QVERIFY(!thread[x].isFinished());
363 }
364
365 readWriteLock.lockForWrite();
366 cond1.wakeAll();
367 cond2.wakeAll();
368 readWriteLock.unlock();
369
370 for (x = 0; x < ThreadCount; ++x) {
371 QVERIFY(thread[x].wait(1000));
372 }
373 }
374 }
375}
376
377class WakeThreadBase : public TerminatingThread
378{
379public:
380 QAtomicInt *count;
381
382 WakeThreadBase() : count(nullptr) {}
383};
384
385class wake_Thread : public WakeThreadBase
386{
387public:
388 QWaitCondition started;
389 QWaitCondition dummy;
390
391 QMutex *mutex;
392 QWaitCondition *cond;
393
394 inline wake_Thread()
395 : mutex(0), cond(0)
396 { }
397
398 static inline void sleep(ulong s)
399 { QThread::sleep(s); }
400
401 void run()
402 {
403 Q_ASSERT(count);
404 Q_ASSERT(mutex);
405 Q_ASSERT(cond);
406 mutex->lock();
407 ++*count;
408 dummy.wakeOne(); // this wakeup should be lost
409 started.wakeOne();
410 dummy.wakeAll(); // this one too
411 cond->wait(lockedMutex: mutex);
412 --*count;
413 mutex->unlock();
414 }
415};
416
417class wake_Thread_2 : public WakeThreadBase
418{
419public:
420 QWaitCondition started;
421 QWaitCondition dummy;
422
423 QReadWriteLock *readWriteLock;
424 QWaitCondition *cond;
425
426 inline wake_Thread_2()
427 : readWriteLock(0), cond(0)
428 { }
429
430 static inline void sleep(ulong s)
431 { QThread::sleep(s); }
432
433 void run()
434 {
435 Q_ASSERT(count);
436 Q_ASSERT(readWriteLock);
437 Q_ASSERT(cond);
438 readWriteLock->lockForWrite();
439 ++*count;
440 dummy.wakeOne(); // this wakeup should be lost
441 started.wakeOne();
442 dummy.wakeAll(); // this one too
443 cond->wait(lockedReadWriteLock: readWriteLock);
444 --*count;
445 readWriteLock->unlock();
446 }
447};
448
449void tst_QWaitCondition::wakeOne()
450{
451 static const int firstWaitInterval = 1000;
452 static const int waitInterval = 30;
453
454 int x;
455 QAtomicInt count;
456 // wake up threads, one at a time
457 for (int i = 0; i < iterations; ++i) {
458 QMutex mutex;
459 QWaitCondition cond;
460
461 // QMutex
462 wake_Thread thread[ThreadCount];
463 bool thread_exited[ThreadCount];
464
465 QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex_")
466 + QString::number(i) + QLatin1Char('_');
467
468 mutex.lock();
469 for (x = 0; x < ThreadCount; ++x) {
470 thread[x].setObjectName(prefix + QString::number(x));
471 thread[x].count = &count;
472 thread[x].mutex = &mutex;
473 thread[x].cond = &cond;
474 thread_exited[x] = false;
475 thread[x].start();
476 // wait for thread to start
477 QVERIFY(thread[x].started.wait(&mutex, 1000));
478 // make sure wakeups are not queued... if nothing is
479 // waiting at the time of the wakeup, nothing happens
480 QVERIFY(!thread[x].dummy.wait(&mutex, 1));
481 }
482 mutex.unlock();
483
484 QCOMPARE(count.loadRelaxed(), ThreadCount);
485
486 // wake up threads one at a time
487 for (x = 0; x < ThreadCount; ++x) {
488 mutex.lock();
489 cond.wakeOne();
490 QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
491 QVERIFY(!thread[x].dummy.wait(&mutex, 1));
492 mutex.unlock();
493
494 int exited = 0;
495 for (int y = 0; y < ThreadCount; ++y) {
496 if (thread_exited[y])
497 continue;
498 if (thread[y].wait(time: exited > 0 ? waitInterval : firstWaitInterval)) {
499 thread_exited[y] = true;
500 ++exited;
501 }
502 }
503
504 QCOMPARE(exited, 1);
505 QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 1));
506 }
507
508 QCOMPARE(count.loadRelaxed(), 0);
509
510 // QReadWriteLock
511 QReadWriteLock readWriteLock;
512 wake_Thread_2 rwthread[ThreadCount];
513
514 prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_readwritelock_")
515 + QString::number(i) + QLatin1Char('_');
516
517 readWriteLock.lockForWrite();
518 for (x = 0; x < ThreadCount; ++x) {
519 rwthread[x].setObjectName(prefix + QString::number(x));
520 rwthread[x].count = &count;
521 rwthread[x].readWriteLock = &readWriteLock;
522 rwthread[x].cond = &cond;
523 thread_exited[x] = false;
524 rwthread[x].start();
525 // wait for thread to start
526 QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
527 // make sure wakeups are not queued... if nothing is
528 // waiting at the time of the wakeup, nothing happens
529 QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
530 }
531 readWriteLock.unlock();
532
533 QCOMPARE(count.loadRelaxed(), ThreadCount);
534
535 // wake up threads one at a time
536 for (x = 0; x < ThreadCount; ++x) {
537 readWriteLock.lockForWrite();
538 cond.wakeOne();
539 QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
540 QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
541 readWriteLock.unlock();
542
543 int exited = 0;
544 for (int y = 0; y < ThreadCount; ++y) {
545 if (thread_exited[y])
546 continue;
547 if (rwthread[y].wait(time: exited > 0 ? waitInterval : firstWaitInterval)) {
548 thread_exited[y] = true;
549 ++exited;
550 }
551 }
552
553 QCOMPARE(exited, 1);
554 QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 1));
555 }
556
557 QCOMPARE(count.loadRelaxed(), 0);
558 }
559
560 // wake up threads, two at a time
561 for (int i = 0; i < iterations; ++i) {
562 QMutex mutex;
563 QWaitCondition cond;
564
565 // QMutex
566 wake_Thread thread[ThreadCount];
567 bool thread_exited[ThreadCount];
568
569 QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex2_")
570 + QString::number(i) + QLatin1Char('_');
571
572 mutex.lock();
573 for (x = 0; x < ThreadCount; ++x) {
574 thread[x].setObjectName(prefix + QString::number(x));
575 thread[x].count = &count;
576 thread[x].mutex = &mutex;
577 thread[x].cond = &cond;
578 thread_exited[x] = false;
579 thread[x].start();
580 // wait for thread to start
581 QVERIFY(thread[x].started.wait(&mutex, 1000));
582 // make sure wakeups are not queued... if nothing is
583 // waiting at the time of the wakeup, nothing happens
584 QVERIFY(!thread[x].dummy.wait(&mutex, 1));
585 }
586 mutex.unlock();
587
588 QCOMPARE(count.loadRelaxed(), ThreadCount);
589
590 // wake up threads one at a time
591 for (x = 0; x < ThreadCount; x += 2) {
592 mutex.lock();
593 cond.wakeOne();
594 cond.wakeOne();
595 QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
596 QVERIFY(!thread[x].dummy.wait(&mutex, 1));
597 QVERIFY(!thread[x + 1].dummy.wait(&mutex, 1));
598 mutex.unlock();
599
600 int exited = 0;
601 for (int y = 0; y < ThreadCount; ++y) {
602 if (thread_exited[y])
603 continue;
604 if (thread[y].wait(time: exited > 0 ? waitInterval : firstWaitInterval)) {
605 thread_exited[y] = true;
606 ++exited;
607 }
608 }
609
610 QCOMPARE(exited, 2);
611 QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 2));
612 }
613
614 QCOMPARE(count.loadRelaxed(), 0);
615
616 // QReadWriteLock
617 QReadWriteLock readWriteLock;
618 wake_Thread_2 rwthread[ThreadCount];
619
620 prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_readwritelock_")
621 + QString::number(i) + QLatin1Char('_');
622
623 readWriteLock.lockForWrite();
624 for (x = 0; x < ThreadCount; ++x) {
625 rwthread[x].setObjectName(prefix + QString::number(x));
626 rwthread[x].count = &count;
627 rwthread[x].readWriteLock = &readWriteLock;
628 rwthread[x].cond = &cond;
629 thread_exited[x] = false;
630 rwthread[x].start();
631 // wait for thread to start
632 QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
633 // make sure wakeups are not queued... if nothing is
634 // waiting at the time of the wakeup, nothing happens
635 QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
636 }
637 readWriteLock.unlock();
638
639 QCOMPARE(count.loadRelaxed(), ThreadCount);
640
641 // wake up threads one at a time
642 for (x = 0; x < ThreadCount; x += 2) {
643 readWriteLock.lockForWrite();
644 cond.wakeOne();
645 cond.wakeOne();
646 QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
647 QVERIFY(!rwthread[x].dummy.wait(&readWriteLock, 1));
648 QVERIFY(!rwthread[x + 1].dummy.wait(&readWriteLock, 1));
649 readWriteLock.unlock();
650
651 int exited = 0;
652 for (int y = 0; y < ThreadCount; ++y) {
653 if (thread_exited[y])
654 continue;
655 if (rwthread[y].wait(time: exited > 0 ? waitInterval : firstWaitInterval)) {
656 thread_exited[y] = true;
657 ++exited;
658 }
659 }
660
661 QCOMPARE(exited, 2);
662 QCOMPARE(count.loadRelaxed(), ThreadCount - (x + 2));
663 }
664
665 QCOMPARE(count.loadRelaxed(), 0);
666 }
667}
668
669void tst_QWaitCondition::wakeAll()
670{
671 int x;
672 QAtomicInt count;
673 for (int i = 0; i < iterations; ++i) {
674 QMutex mutex;
675 QWaitCondition cond;
676
677 // QMutex
678 wake_Thread thread[ThreadCount];
679
680 QString prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_mutex_")
681 + QString::number(i) + QLatin1Char('_');
682
683 mutex.lock();
684 for (x = 0; x < ThreadCount; ++x) {
685 thread[x].setObjectName(prefix + QString::number(x));
686 thread[x].count = &count;
687 thread[x].mutex = &mutex;
688 thread[x].cond = &cond;
689 thread[x].start();
690 // wait for thread to start
691 QVERIFY(thread[x].started.wait(&mutex, 1000));
692 }
693 mutex.unlock();
694
695 QCOMPARE(count.loadRelaxed(), ThreadCount);
696
697 // wake up all threads at once
698 mutex.lock();
699 cond.wakeAll();
700 QVERIFY(!cond.wait(&mutex, COND_WAIT_TIME));
701 mutex.unlock();
702
703 int exited = 0;
704 for (x = 0; x < ThreadCount; ++x) {
705 if (thread[x].wait(time: 1000))
706 ++exited;
707 }
708
709 QCOMPARE(exited, ThreadCount);
710 QCOMPARE(count.loadRelaxed(), 0);
711
712 // QReadWriteLock
713 QReadWriteLock readWriteLock;
714 wake_Thread_2 rwthread[ThreadCount];
715
716 prefix = QLatin1String(QTest::currentTestFunction()) + QLatin1String("_readwritelock_")
717 + QString::number(i) + QLatin1Char('_');
718
719 readWriteLock.lockForWrite();
720 for (x = 0; x < ThreadCount; ++x) {
721 rwthread[x].setObjectName(prefix + QString::number(x));
722 rwthread[x].count = &count;
723 rwthread[x].readWriteLock = &readWriteLock;
724 rwthread[x].cond = &cond;
725 rwthread[x].start();
726 // wait for thread to start
727 QVERIFY(rwthread[x].started.wait(&readWriteLock, 1000));
728 }
729 readWriteLock.unlock();
730
731 QCOMPARE(count.loadRelaxed(), ThreadCount);
732
733 // wake up all threads at once
734 readWriteLock.lockForWrite();
735 cond.wakeAll();
736 QVERIFY(!cond.wait(&readWriteLock, COND_WAIT_TIME));
737 readWriteLock.unlock();
738
739 exited = 0;
740 for (x = 0; x < ThreadCount; ++x) {
741 if (rwthread[x].wait(time: 1000))
742 ++exited;
743 }
744
745 QCOMPARE(exited, ThreadCount);
746 QCOMPARE(count.loadRelaxed(), 0);
747 }
748}
749
750class wait_RaceConditionThread : public TerminatingThread
751{
752public:
753 wait_RaceConditionThread(QMutex *mutex, QWaitCondition *startup, QWaitCondition *waitCondition,
754 ulong timeout = ULONG_MAX)
755 : timeout(timeout), returnValue(false), ready(false),
756 mutex(mutex), startup(startup), waitCondition(waitCondition) {}
757
758 unsigned long timeout;
759 bool returnValue;
760
761 bool ready;
762
763 QMutex *mutex;
764 QWaitCondition *startup;
765 QWaitCondition *waitCondition;
766
767 void run() {
768 mutex->lock();
769
770 ready = true;
771 startup->wakeOne();
772
773 returnValue = waitCondition->wait(lockedMutex: mutex, time: timeout);
774
775 mutex->unlock();
776 }
777};
778
779class wait_RaceConditionThread_2 : public TerminatingThread
780{
781public:
782 wait_RaceConditionThread_2(QReadWriteLock *readWriteLock,
783 QWaitCondition *startup,
784 QWaitCondition *waitCondition,
785 ulong timeout = ULONG_MAX)
786 : timeout(timeout), returnValue(false), ready(false),
787 readWriteLock(readWriteLock), startup(startup), waitCondition(waitCondition)
788 { }
789
790 unsigned long timeout;
791 bool returnValue;
792
793 bool ready;
794
795 QReadWriteLock *readWriteLock;
796 QWaitCondition *startup;
797 QWaitCondition *waitCondition;
798
799 void run() {
800 readWriteLock->lockForWrite();
801
802 ready = true;
803 startup->wakeOne();
804
805 returnValue = waitCondition->wait(lockedReadWriteLock: readWriteLock, time: timeout);
806
807 readWriteLock->unlock();
808 }
809};
810
811void tst_QWaitCondition::wait_RaceCondition()
812{
813 {
814 QMutex mutex;
815 QWaitCondition startup;
816 QWaitCondition waitCondition;
817
818 wait_RaceConditionThread timeoutThread(&mutex, &startup, &waitCondition, 1000),
819 waitingThread1(&mutex, &startup, &waitCondition);
820
821 timeoutThread.start();
822 waitingThread1.start();
823 mutex.lock();
824
825 // wait for the threads to start up
826 while (!timeoutThread.ready
827 || !waitingThread1.ready) {
828 startup.wait(lockedMutex: &mutex);
829 }
830
831 QTest::qWait(ms: 2000);
832
833 waitCondition.wakeOne();
834
835 mutex.unlock();
836
837 QVERIFY(timeoutThread.wait(5000));
838 QVERIFY(!timeoutThread.returnValue);
839 QVERIFY(waitingThread1.wait(5000));
840 QVERIFY(waitingThread1.returnValue);
841 }
842
843 {
844 QReadWriteLock readWriteLock;
845 QWaitCondition startup;
846 QWaitCondition waitCondition;
847
848 wait_RaceConditionThread_2 timeoutThread(&readWriteLock, &startup, &waitCondition, 1000),
849 waitingThread1(&readWriteLock, &startup, &waitCondition);
850
851 timeoutThread.start();
852 waitingThread1.start();
853 readWriteLock.lockForRead();
854
855 // wait for the threads to start up
856 while (!timeoutThread.ready
857 || !waitingThread1.ready) {
858 startup.wait(lockedReadWriteLock: &readWriteLock);
859 }
860
861 QTest::qWait(ms: 2000);
862
863 waitCondition.wakeOne();
864
865 readWriteLock.unlock();
866
867 QVERIFY(timeoutThread.wait(5000));
868 QVERIFY(!timeoutThread.returnValue);
869 QVERIFY(waitingThread1.wait(5000));
870 QVERIFY(waitingThread1.returnValue);
871 }
872}
873
874QTEST_MAIN(tst_QWaitCondition)
875#include "tst_qwaitcondition.moc"
876

source code of qtbase/tests/auto/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp