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 | |
39 | class tst_QWaitCondition : public QObject |
40 | { |
41 | Q_OBJECT |
42 | private slots: |
43 | void wait_QMutex(); |
44 | void wait_QReadWriteLock(); |
45 | void wakeOne(); |
46 | void wakeAll(); |
47 | void wait_RaceCondition(); |
48 | }; |
49 | |
50 | static const int iterations = 4; |
51 | static const int ThreadCount = 4; |
52 | |
53 | // Terminate thread in destructor for threads instantiated on the stack |
54 | class TerminatingThread : public QThread |
55 | { |
56 | public: |
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 | |
71 | class wait_QMutex_Thread_1 : public TerminatingThread |
72 | { |
73 | public: |
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 | |
89 | class wait_QMutex_Thread_2 : public TerminatingThread |
90 | { |
91 | public: |
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 | |
110 | class wait_QReadWriteLock_Thread_1 : public TerminatingThread |
111 | { |
112 | public: |
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 | |
128 | class wait_QReadWriteLock_Thread_2 : public TerminatingThread |
129 | { |
130 | public: |
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 | |
149 | void 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 | |
237 | void 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 | |
377 | class WakeThreadBase : public TerminatingThread |
378 | { |
379 | public: |
380 | QAtomicInt *count; |
381 | |
382 | WakeThreadBase() : count(nullptr) {} |
383 | }; |
384 | |
385 | class wake_Thread : public WakeThreadBase |
386 | { |
387 | public: |
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 | |
417 | class wake_Thread_2 : public WakeThreadBase |
418 | { |
419 | public: |
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 | |
449 | void 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 | |
669 | void 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 | |
750 | class wait_RaceConditionThread : public TerminatingThread |
751 | { |
752 | public: |
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 | |
779 | class wait_RaceConditionThread_2 : public TerminatingThread |
780 | { |
781 | public: |
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 | |
811 | void 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 | |
874 | QTEST_MAIN(tst_QWaitCondition) |
875 | #include "tst_qwaitcondition.moc" |
876 | |