1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2021 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 <qcoreapplication.h> |
32 | #include <qelapsedtimer.h> |
33 | #include <qmutex.h> |
34 | #include <qthread.h> |
35 | #include <qtimer.h> |
36 | #include <qwaitcondition.h> |
37 | #include <qdebug.h> |
38 | #include <qmetaobject.h> |
39 | #include <qscopeguard.h> |
40 | |
41 | #ifdef Q_OS_UNIX |
42 | #include <pthread.h> |
43 | #endif |
44 | #if defined(Q_OS_WIN) |
45 | #include <windows.h> |
46 | #if defined(Q_OS_WIN32) |
47 | #include <process.h> |
48 | #endif |
49 | #endif |
50 | |
51 | #ifndef QT_NO_EXCEPTIONS |
52 | #include <exception> |
53 | #endif |
54 | |
55 | #include "emulationdetector.h" |
56 | |
57 | class tst_QThread : public QObject |
58 | { |
59 | Q_OBJECT |
60 | private slots: |
61 | void currentThreadId(); |
62 | void currentThread(); |
63 | void idealThreadCount(); |
64 | void isFinished(); |
65 | void isRunning(); |
66 | void setPriority(); |
67 | void setStackSize(); |
68 | void exit(); |
69 | void start(); |
70 | void terminate(); |
71 | void quit(); |
72 | void started(); |
73 | void finished(); |
74 | void terminated(); // Named after a signal that was removed in Qt 5.0 |
75 | void exec(); |
76 | void sleep(); |
77 | void msleep(); |
78 | void usleep(); |
79 | |
80 | void nativeThreadAdoption(); |
81 | void adoptedThreadAffinity(); |
82 | void adoptedThreadSetPriority(); |
83 | void adoptedThreadExit(); |
84 | void adoptedThreadExec(); |
85 | void adoptedThreadFinished(); |
86 | void adoptedThreadExecFinished(); |
87 | void adoptMultipleThreads(); |
88 | void adoptMultipleThreadsOverlap(); |
89 | |
90 | void exitAndStart(); |
91 | void exitAndExec(); |
92 | |
93 | void connectThreadFinishedSignalToObjectDeleteLaterSlot(); |
94 | void wait2(); |
95 | void wait3_slowDestructor(); |
96 | void destroyFinishRace(); |
97 | void startFinishRace(); |
98 | void startAndQuitCustomEventLoop(); |
99 | void isRunningInFinished(); |
100 | |
101 | void customEventDispatcher(); |
102 | |
103 | void requestTermination(); |
104 | |
105 | void stressTest(); |
106 | |
107 | void quitLock(); |
108 | |
109 | void create(); |
110 | void threadIdReuse(); |
111 | }; |
112 | |
113 | enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute }; |
114 | |
115 | template <class Int> |
116 | static QString msgElapsed(Int elapsed) |
117 | { |
118 | return QString::fromLatin1(str: "elapsed: %1" ).arg(elapsed); |
119 | } |
120 | |
121 | class SignalRecorder : public QObject |
122 | { |
123 | Q_OBJECT |
124 | public: |
125 | QAtomicInt activationCount; |
126 | |
127 | inline SignalRecorder() |
128 | : activationCount(0) |
129 | { } |
130 | |
131 | bool wasActivated() |
132 | { return activationCount.loadRelaxed() > 0; } |
133 | |
134 | public slots: |
135 | void slot(); |
136 | }; |
137 | |
138 | void SignalRecorder::slot() |
139 | { activationCount.ref(); } |
140 | |
141 | class Current_Thread : public QThread |
142 | { |
143 | public: |
144 | Qt::HANDLE id; |
145 | QThread *thread; |
146 | |
147 | void run() |
148 | { |
149 | id = QThread::currentThreadId(); |
150 | thread = QThread::currentThread(); |
151 | } |
152 | }; |
153 | |
154 | class Simple_Thread : public QThread |
155 | { |
156 | public: |
157 | QMutex mutex; |
158 | QWaitCondition cond; |
159 | |
160 | void run() |
161 | { |
162 | QMutexLocker locker(&mutex); |
163 | cond.wakeOne(); |
164 | } |
165 | }; |
166 | |
167 | class Exit_Object : public QObject |
168 | { |
169 | Q_OBJECT |
170 | public: |
171 | QThread *thread; |
172 | int code; |
173 | public slots: |
174 | void slot() |
175 | { thread->exit(retcode: code); } |
176 | }; |
177 | |
178 | class Exit_Thread : public Simple_Thread |
179 | { |
180 | public: |
181 | Exit_Object *object; |
182 | int code; |
183 | int result; |
184 | |
185 | void run() |
186 | { |
187 | Simple_Thread::run(); |
188 | if (object) { |
189 | object->thread = this; |
190 | object->code = code; |
191 | QTimer::singleShot(msec: 100, receiver: object, SLOT(slot())); |
192 | } |
193 | result = exec(); |
194 | } |
195 | }; |
196 | |
197 | class Terminate_Thread : public Simple_Thread |
198 | { |
199 | public: |
200 | void run() |
201 | { |
202 | setTerminationEnabled(false); |
203 | { |
204 | QMutexLocker locker(&mutex); |
205 | cond.wakeOne(); |
206 | cond.wait(lockedMutex: &mutex, time: five_minutes); |
207 | } |
208 | setTerminationEnabled(true); |
209 | qFatal(msg: "tst_QThread: test case hung" ); |
210 | } |
211 | }; |
212 | |
213 | class Quit_Object : public QObject |
214 | { |
215 | Q_OBJECT |
216 | public: |
217 | QThread *thread; |
218 | public slots: |
219 | void slot() |
220 | { thread->quit(); } |
221 | }; |
222 | |
223 | class Quit_Thread : public Simple_Thread |
224 | { |
225 | public: |
226 | Quit_Object *object; |
227 | int result; |
228 | |
229 | void run() |
230 | { |
231 | Simple_Thread::run(); |
232 | if (object) { |
233 | object->thread = this; |
234 | QTimer::singleShot(msec: 100, receiver: object, SLOT(slot())); |
235 | } |
236 | result = exec(); |
237 | } |
238 | }; |
239 | |
240 | class Sleep_Thread : public Simple_Thread |
241 | { |
242 | public: |
243 | enum SleepType { Second, Millisecond, Microsecond }; |
244 | |
245 | SleepType sleepType; |
246 | int interval; |
247 | |
248 | int elapsed; // result, in *MILLISECONDS* |
249 | |
250 | void run() |
251 | { |
252 | QMutexLocker locker(&mutex); |
253 | |
254 | elapsed = 0; |
255 | QElapsedTimer timer; |
256 | timer.start(); |
257 | switch (sleepType) { |
258 | case Second: |
259 | sleep(interval); |
260 | break; |
261 | case Millisecond: |
262 | msleep(interval); |
263 | break; |
264 | case Microsecond: |
265 | usleep(interval); |
266 | break; |
267 | } |
268 | elapsed = timer.elapsed(); |
269 | |
270 | cond.wakeOne(); |
271 | } |
272 | }; |
273 | |
274 | void tst_QThread::currentThreadId() |
275 | { |
276 | Current_Thread thread; |
277 | thread.id = 0; |
278 | thread.thread = 0; |
279 | thread.start(); |
280 | QVERIFY(thread.wait(five_minutes)); |
281 | QVERIFY(thread.id != 0); |
282 | QVERIFY(thread.id != QThread::currentThreadId()); |
283 | } |
284 | |
285 | void tst_QThread::currentThread() |
286 | { |
287 | QVERIFY(QThread::currentThread() != 0); |
288 | QCOMPARE(QThread::currentThread(), thread()); |
289 | |
290 | Current_Thread thread; |
291 | thread.id = 0; |
292 | thread.thread = 0; |
293 | thread.start(); |
294 | QVERIFY(thread.wait(five_minutes)); |
295 | QCOMPARE(thread.thread, (QThread *)&thread); |
296 | } |
297 | |
298 | void tst_QThread::idealThreadCount() |
299 | { |
300 | QVERIFY(QThread::idealThreadCount() > 0); |
301 | qDebug() << "Ideal thread count:" << QThread::idealThreadCount(); |
302 | } |
303 | |
304 | void tst_QThread::isFinished() |
305 | { |
306 | Simple_Thread thread; |
307 | QVERIFY(!thread.isFinished()); |
308 | QMutexLocker locker(&thread.mutex); |
309 | thread.start(); |
310 | QVERIFY(!thread.isFinished()); |
311 | thread.cond.wait(lockedMutex: locker.mutex()); |
312 | QVERIFY(thread.wait(five_minutes)); |
313 | QVERIFY(thread.isFinished()); |
314 | } |
315 | |
316 | void tst_QThread::isRunning() |
317 | { |
318 | Simple_Thread thread; |
319 | QVERIFY(!thread.isRunning()); |
320 | QMutexLocker locker(&thread.mutex); |
321 | thread.start(); |
322 | QVERIFY(thread.isRunning()); |
323 | thread.cond.wait(lockedMutex: locker.mutex()); |
324 | QVERIFY(thread.wait(five_minutes)); |
325 | QVERIFY(!thread.isRunning()); |
326 | } |
327 | |
328 | void tst_QThread::setPriority() |
329 | { |
330 | Simple_Thread thread; |
331 | |
332 | // cannot change the priority, since the thread is not running |
333 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
334 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
335 | thread.setPriority(QThread::IdlePriority); |
336 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
337 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
338 | thread.setPriority(QThread::LowestPriority); |
339 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
340 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
341 | thread.setPriority(QThread::LowPriority); |
342 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
343 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
344 | thread.setPriority(QThread::NormalPriority); |
345 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
346 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
347 | thread.setPriority(QThread::HighPriority); |
348 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
349 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
350 | thread.setPriority(QThread::HighestPriority); |
351 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
352 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
353 | thread.setPriority(QThread::TimeCriticalPriority); |
354 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
355 | |
356 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
357 | QMutexLocker locker(&thread.mutex); |
358 | thread.start(); |
359 | |
360 | // change the priority of a running thread |
361 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
362 | thread.setPriority(QThread::IdlePriority); |
363 | QCOMPARE(thread.priority(), QThread::IdlePriority); |
364 | thread.setPriority(QThread::LowestPriority); |
365 | QCOMPARE(thread.priority(), QThread::LowestPriority); |
366 | thread.setPriority(QThread::LowPriority); |
367 | QCOMPARE(thread.priority(), QThread::LowPriority); |
368 | thread.setPriority(QThread::NormalPriority); |
369 | QCOMPARE(thread.priority(), QThread::NormalPriority); |
370 | thread.setPriority(QThread::HighPriority); |
371 | QCOMPARE(thread.priority(), QThread::HighPriority); |
372 | thread.setPriority(QThread::HighestPriority); |
373 | QCOMPARE(thread.priority(), QThread::HighestPriority); |
374 | thread.setPriority(QThread::TimeCriticalPriority); |
375 | QCOMPARE(thread.priority(), QThread::TimeCriticalPriority); |
376 | thread.cond.wait(lockedMutex: locker.mutex()); |
377 | QVERIFY(thread.wait(five_minutes)); |
378 | |
379 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
380 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
381 | thread.setPriority(QThread::IdlePriority); |
382 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
383 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
384 | thread.setPriority(QThread::LowestPriority); |
385 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
386 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
387 | thread.setPriority(QThread::LowPriority); |
388 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
389 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
390 | thread.setPriority(QThread::NormalPriority); |
391 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
392 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
393 | thread.setPriority(QThread::HighPriority); |
394 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
395 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
396 | thread.setPriority(QThread::HighestPriority); |
397 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
398 | QTest::ignoreMessage(type: QtWarningMsg, message: "QThread::setPriority: Cannot set priority, thread is not running" ); |
399 | thread.setPriority(QThread::TimeCriticalPriority); |
400 | QCOMPARE(thread.priority(), QThread::InheritPriority); |
401 | } |
402 | |
403 | void tst_QThread::setStackSize() |
404 | { |
405 | Simple_Thread thread; |
406 | QCOMPARE(thread.stackSize(), 0u); |
407 | thread.setStackSize(8192u); |
408 | QCOMPARE(thread.stackSize(), 8192u); |
409 | thread.setStackSize(0u); |
410 | QCOMPARE(thread.stackSize(), 0u); |
411 | } |
412 | |
413 | void tst_QThread::exit() |
414 | { |
415 | Exit_Thread thread; |
416 | thread.object = new Exit_Object; |
417 | thread.object->moveToThread(thread: &thread); |
418 | thread.code = 42; |
419 | thread.result = 0; |
420 | QVERIFY(!thread.isFinished()); |
421 | QVERIFY(!thread.isRunning()); |
422 | QMutexLocker locker(&thread.mutex); |
423 | thread.start(); |
424 | QVERIFY(thread.isRunning()); |
425 | QVERIFY(!thread.isFinished()); |
426 | thread.cond.wait(lockedMutex: locker.mutex()); |
427 | QVERIFY(thread.wait(five_minutes)); |
428 | QVERIFY(thread.isFinished()); |
429 | QVERIFY(!thread.isRunning()); |
430 | QCOMPARE(thread.result, thread.code); |
431 | delete thread.object; |
432 | |
433 | Exit_Thread thread2; |
434 | thread2.object = 0; |
435 | thread2.code = 53; |
436 | thread2.result = 0; |
437 | QMutexLocker locker2(&thread2.mutex); |
438 | thread2.start(); |
439 | thread2.exit(retcode: thread2.code); |
440 | thread2.cond.wait(lockedMutex: locker2.mutex()); |
441 | QVERIFY(thread2.wait(five_minutes)); |
442 | QCOMPARE(thread2.result, thread2.code); |
443 | } |
444 | |
445 | void tst_QThread::start() |
446 | { |
447 | QThread::Priority priorities[] = { |
448 | QThread::IdlePriority, |
449 | QThread::LowestPriority, |
450 | QThread::LowPriority, |
451 | QThread::NormalPriority, |
452 | QThread::HighPriority, |
453 | QThread::HighestPriority, |
454 | QThread::TimeCriticalPriority, |
455 | QThread::InheritPriority |
456 | }; |
457 | const int prio_count = sizeof(priorities) / sizeof(QThread::Priority); |
458 | |
459 | for (int i = 0; i < prio_count; ++i) { |
460 | Simple_Thread thread; |
461 | QVERIFY(!thread.isFinished()); |
462 | QVERIFY(!thread.isRunning()); |
463 | QMutexLocker locker(&thread.mutex); |
464 | thread.start(priorities[i]); |
465 | QVERIFY(thread.isRunning()); |
466 | QVERIFY(!thread.isFinished()); |
467 | thread.cond.wait(lockedMutex: locker.mutex()); |
468 | QVERIFY(thread.wait(five_minutes)); |
469 | QVERIFY(thread.isFinished()); |
470 | QVERIFY(!thread.isRunning()); |
471 | } |
472 | } |
473 | |
474 | void tst_QThread::terminate() |
475 | { |
476 | #if defined(Q_OS_WINRT) || defined(Q_OS_ANDROID) |
477 | QSKIP("Thread termination is not supported on WinRT or Android." ); |
478 | #endif |
479 | Terminate_Thread thread; |
480 | { |
481 | QMutexLocker locker(&thread.mutex); |
482 | thread.start(); |
483 | QVERIFY(thread.cond.wait(locker.mutex(), five_minutes)); |
484 | thread.terminate(); |
485 | thread.cond.wakeOne(); |
486 | } |
487 | QVERIFY(thread.wait(five_minutes)); |
488 | } |
489 | |
490 | void tst_QThread::quit() |
491 | { |
492 | Quit_Thread thread; |
493 | thread.object = new Quit_Object; |
494 | thread.object->moveToThread(thread: &thread); |
495 | thread.result = -1; |
496 | QVERIFY(!thread.isFinished()); |
497 | QVERIFY(!thread.isRunning()); |
498 | QMutexLocker locker(&thread.mutex); |
499 | thread.start(); |
500 | QVERIFY(thread.isRunning()); |
501 | QVERIFY(!thread.isFinished()); |
502 | thread.cond.wait(lockedMutex: locker.mutex()); |
503 | QVERIFY(thread.wait(five_minutes)); |
504 | QVERIFY(thread.isFinished()); |
505 | QVERIFY(!thread.isRunning()); |
506 | QCOMPARE(thread.result, 0); |
507 | delete thread.object; |
508 | |
509 | Quit_Thread thread2; |
510 | thread2.object = 0; |
511 | thread2.result = -1; |
512 | QMutexLocker locker2(&thread2.mutex); |
513 | thread2.start(); |
514 | thread2.quit(); |
515 | thread2.cond.wait(lockedMutex: locker2.mutex()); |
516 | QVERIFY(thread2.wait(five_minutes)); |
517 | QCOMPARE(thread2.result, 0); |
518 | } |
519 | |
520 | void tst_QThread::started() |
521 | { |
522 | SignalRecorder recorder; |
523 | Simple_Thread thread; |
524 | connect(sender: &thread, SIGNAL(started()), receiver: &recorder, SLOT(slot()), Qt::DirectConnection); |
525 | thread.start(); |
526 | QVERIFY(thread.wait(five_minutes)); |
527 | QVERIFY(recorder.wasActivated()); |
528 | } |
529 | |
530 | void tst_QThread::finished() |
531 | { |
532 | SignalRecorder recorder; |
533 | Simple_Thread thread; |
534 | connect(sender: &thread, SIGNAL(finished()), receiver: &recorder, SLOT(slot()), Qt::DirectConnection); |
535 | thread.start(); |
536 | QVERIFY(thread.wait(five_minutes)); |
537 | QVERIFY(recorder.wasActivated()); |
538 | } |
539 | |
540 | void tst_QThread::terminated() |
541 | { |
542 | #if defined(Q_OS_WINRT) || defined(Q_OS_ANDROID) |
543 | QSKIP("Thread termination is not supported on WinRT or Android." ); |
544 | #endif |
545 | SignalRecorder recorder; |
546 | Terminate_Thread thread; |
547 | connect(sender: &thread, SIGNAL(finished()), receiver: &recorder, SLOT(slot()), Qt::DirectConnection); |
548 | { |
549 | QMutexLocker locker(&thread.mutex); |
550 | thread.start(); |
551 | thread.cond.wait(lockedMutex: locker.mutex()); |
552 | thread.terminate(); |
553 | thread.cond.wakeOne(); |
554 | } |
555 | QVERIFY(thread.wait(five_minutes)); |
556 | QVERIFY(recorder.wasActivated()); |
557 | } |
558 | |
559 | void tst_QThread::exec() |
560 | { |
561 | class MultipleExecThread : public QThread |
562 | { |
563 | public: |
564 | int res1, res2; |
565 | |
566 | MultipleExecThread() : res1(-2), res2(-2) { } |
567 | |
568 | void run() |
569 | { |
570 | { |
571 | Exit_Object o; |
572 | o.thread = this; |
573 | o.code = 1; |
574 | QTimer::singleShot(msec: 100, receiver: &o, SLOT(slot())); |
575 | res1 = exec(); |
576 | } |
577 | { |
578 | Exit_Object o; |
579 | o.thread = this; |
580 | o.code = 2; |
581 | QTimer::singleShot(msec: 100, receiver: &o, SLOT(slot())); |
582 | res2 = exec(); |
583 | } |
584 | } |
585 | }; |
586 | |
587 | MultipleExecThread thread; |
588 | thread.start(); |
589 | QVERIFY(thread.wait()); |
590 | |
591 | QCOMPARE(thread.res1, 1); |
592 | QCOMPARE(thread.res2, 2); |
593 | } |
594 | |
595 | void tst_QThread::sleep() |
596 | { |
597 | Sleep_Thread thread; |
598 | thread.sleepType = Sleep_Thread::Second; |
599 | thread.interval = 2; |
600 | thread.start(); |
601 | QVERIFY(thread.wait(five_minutes)); |
602 | QVERIFY2(thread.elapsed >= 2000, qPrintable(msgElapsed(thread.elapsed))); |
603 | } |
604 | |
605 | void tst_QThread::msleep() |
606 | { |
607 | Sleep_Thread thread; |
608 | thread.sleepType = Sleep_Thread::Millisecond; |
609 | thread.interval = 120; |
610 | thread.start(); |
611 | QVERIFY(thread.wait(five_minutes)); |
612 | #if defined (Q_OS_WIN) // May no longer be needed |
613 | QVERIFY2(thread.elapsed >= 100, qPrintable(msgElapsed(thread.elapsed))); |
614 | #else |
615 | QVERIFY2(thread.elapsed >= 120, qPrintable(msgElapsed(thread.elapsed))); |
616 | #endif |
617 | } |
618 | |
619 | void tst_QThread::usleep() |
620 | { |
621 | Sleep_Thread thread; |
622 | thread.sleepType = Sleep_Thread::Microsecond; |
623 | thread.interval = 120000; |
624 | thread.start(); |
625 | QVERIFY(thread.wait(five_minutes)); |
626 | #if defined (Q_OS_WIN) // May no longer be needed |
627 | QVERIFY2(thread.elapsed >= 100, qPrintable(msgElapsed(thread.elapsed))); |
628 | #else |
629 | QVERIFY2(thread.elapsed >= 120, qPrintable(msgElapsed(thread.elapsed))); |
630 | #endif |
631 | } |
632 | |
633 | typedef void (*FunctionPointer)(void *); |
634 | void noop(void*) { } |
635 | |
636 | #if defined Q_OS_UNIX |
637 | typedef pthread_t ThreadHandle; |
638 | #elif defined Q_OS_WIN |
639 | typedef HANDLE ThreadHandle; |
640 | #endif |
641 | |
642 | #ifdef Q_OS_WIN |
643 | #define WIN_FIX_STDCALL __stdcall |
644 | #else |
645 | #define WIN_FIX_STDCALL |
646 | #endif |
647 | |
648 | class NativeThreadWrapper |
649 | { |
650 | public: |
651 | NativeThreadWrapper() : qthread(0), waitForStop(false) {} |
652 | void start(FunctionPointer functionPointer = noop, void *data = 0); |
653 | void startAndWait(FunctionPointer functionPointer = noop, void *data = 0); |
654 | void join(); |
655 | void setWaitForStop() { waitForStop = true; } |
656 | void stop(); |
657 | |
658 | ThreadHandle nativeThreadHandle; |
659 | QThread *qthread; |
660 | QWaitCondition startCondition; |
661 | QMutex mutex; |
662 | bool waitForStop; |
663 | QWaitCondition stopCondition; |
664 | protected: |
665 | static void *runUnix(void *data); |
666 | static unsigned WIN_FIX_STDCALL runWin(void *data); |
667 | |
668 | FunctionPointer functionPointer; |
669 | void *data; |
670 | }; |
671 | |
672 | void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data) |
673 | { |
674 | this->functionPointer = functionPointer; |
675 | this->data = data; |
676 | #if defined Q_OS_UNIX |
677 | const int state = pthread_create(newthread: &nativeThreadHandle, attr: 0, start_routine: NativeThreadWrapper::runUnix, arg: this); |
678 | Q_UNUSED(state); |
679 | #elif defined(Q_OS_WINRT) |
680 | nativeThreadHandle = CreateThread(NULL, 0 , (LPTHREAD_START_ROUTINE)NativeThreadWrapper::runWin , this, 0, NULL); |
681 | #elif defined Q_OS_WIN |
682 | unsigned thrdid = 0; |
683 | nativeThreadHandle = (Qt::HANDLE) _beginthreadex(NULL, 0, NativeThreadWrapper::runWin, this, 0, &thrdid); |
684 | #endif |
685 | } |
686 | |
687 | void NativeThreadWrapper::startAndWait(FunctionPointer functionPointer, void *data) |
688 | { |
689 | QMutexLocker locker(&mutex); |
690 | start(functionPointer, data); |
691 | startCondition.wait(lockedMutex: locker.mutex()); |
692 | } |
693 | |
694 | void NativeThreadWrapper::join() |
695 | { |
696 | #if defined Q_OS_UNIX |
697 | pthread_join(th: nativeThreadHandle, thread_return: 0); |
698 | #elif defined Q_OS_WIN |
699 | WaitForSingleObjectEx(nativeThreadHandle, INFINITE, FALSE); |
700 | CloseHandle(nativeThreadHandle); |
701 | #endif |
702 | } |
703 | |
704 | void *NativeThreadWrapper::runUnix(void *that) |
705 | { |
706 | NativeThreadWrapper *nativeThreadWrapper = reinterpret_cast<NativeThreadWrapper*>(that); |
707 | |
708 | // Adopt thread, create QThread object. |
709 | nativeThreadWrapper->qthread = QThread::currentThread(); |
710 | |
711 | // Release main thread. |
712 | { |
713 | QMutexLocker lock(&nativeThreadWrapper->mutex); |
714 | nativeThreadWrapper->startCondition.wakeOne(); |
715 | } |
716 | |
717 | // Run function. |
718 | nativeThreadWrapper->functionPointer(nativeThreadWrapper->data); |
719 | |
720 | // Wait for stop. |
721 | { |
722 | QMutexLocker lock(&nativeThreadWrapper->mutex); |
723 | if (nativeThreadWrapper->waitForStop) |
724 | nativeThreadWrapper->stopCondition.wait(lockedMutex: lock.mutex()); |
725 | } |
726 | |
727 | return 0; |
728 | } |
729 | |
730 | unsigned WIN_FIX_STDCALL NativeThreadWrapper::runWin(void *data) |
731 | { |
732 | runUnix(that: data); |
733 | return 0; |
734 | } |
735 | |
736 | void NativeThreadWrapper::stop() |
737 | { |
738 | QMutexLocker lock(&mutex); |
739 | waitForStop = false; |
740 | stopCondition.wakeOne(); |
741 | } |
742 | |
743 | bool threadAdoptedOk = false; |
744 | QThread *mainThread; |
745 | void testNativeThreadAdoption(void *) |
746 | { |
747 | threadAdoptedOk = (QThread::currentThreadId() != 0 |
748 | && QThread::currentThread() != 0 |
749 | && QThread::currentThread() != mainThread); |
750 | } |
751 | void tst_QThread::nativeThreadAdoption() |
752 | { |
753 | threadAdoptedOk = false; |
754 | mainThread = QThread::currentThread(); |
755 | NativeThreadWrapper nativeThread; |
756 | nativeThread.setWaitForStop(); |
757 | nativeThread.startAndWait(functionPointer: testNativeThreadAdoption); |
758 | QVERIFY(nativeThread.qthread); |
759 | |
760 | nativeThread.stop(); |
761 | nativeThread.join(); |
762 | |
763 | QVERIFY(threadAdoptedOk); |
764 | } |
765 | |
766 | void adoptedThreadAffinityFunction(void *arg) |
767 | { |
768 | QThread **affinity = reinterpret_cast<QThread **>(arg); |
769 | QThread *current = QThread::currentThread(); |
770 | affinity[0] = current; |
771 | affinity[1] = current->thread(); |
772 | } |
773 | |
774 | void tst_QThread::adoptedThreadAffinity() |
775 | { |
776 | QThread *affinity[2] = { 0, 0 }; |
777 | |
778 | NativeThreadWrapper thread; |
779 | thread.startAndWait(functionPointer: adoptedThreadAffinityFunction, data: affinity); |
780 | thread.join(); |
781 | |
782 | // adopted thread should have affinity to itself |
783 | QCOMPARE(affinity[0], affinity[1]); |
784 | } |
785 | |
786 | void tst_QThread::adoptedThreadSetPriority() |
787 | { |
788 | NativeThreadWrapper nativeThread; |
789 | nativeThread.setWaitForStop(); |
790 | nativeThread.startAndWait(); |
791 | |
792 | // change the priority of a running thread |
793 | QCOMPARE(nativeThread.qthread->priority(), QThread::InheritPriority); |
794 | nativeThread.qthread->setPriority(QThread::IdlePriority); |
795 | QCOMPARE(nativeThread.qthread->priority(), QThread::IdlePriority); |
796 | nativeThread.qthread->setPriority(QThread::LowestPriority); |
797 | QCOMPARE(nativeThread.qthread->priority(), QThread::LowestPriority); |
798 | nativeThread.qthread->setPriority(QThread::LowPriority); |
799 | QCOMPARE(nativeThread.qthread->priority(), QThread::LowPriority); |
800 | nativeThread.qthread->setPriority(QThread::NormalPriority); |
801 | QCOMPARE(nativeThread.qthread->priority(), QThread::NormalPriority); |
802 | nativeThread.qthread->setPriority(QThread::HighPriority); |
803 | QCOMPARE(nativeThread.qthread->priority(), QThread::HighPriority); |
804 | nativeThread.qthread->setPriority(QThread::HighestPriority); |
805 | QCOMPARE(nativeThread.qthread->priority(), QThread::HighestPriority); |
806 | nativeThread.qthread->setPriority(QThread::TimeCriticalPriority); |
807 | QCOMPARE(nativeThread.qthread->priority(), QThread::TimeCriticalPriority); |
808 | |
809 | nativeThread.stop(); |
810 | nativeThread.join(); |
811 | } |
812 | |
813 | void tst_QThread::adoptedThreadExit() |
814 | { |
815 | NativeThreadWrapper nativeThread; |
816 | nativeThread.setWaitForStop(); |
817 | |
818 | nativeThread.startAndWait(); |
819 | QVERIFY(nativeThread.qthread); |
820 | QVERIFY(nativeThread.qthread->isRunning()); |
821 | QVERIFY(!nativeThread.qthread->isFinished()); |
822 | |
823 | nativeThread.stop(); |
824 | nativeThread.join(); |
825 | } |
826 | |
827 | void adoptedThreadExecFunction(void *) |
828 | { |
829 | QThread * const adoptedThread = QThread::currentThread(); |
830 | QEventLoop eventLoop(adoptedThread); |
831 | |
832 | const int code = 1; |
833 | Exit_Object o; |
834 | o.thread = adoptedThread; |
835 | o.code = code; |
836 | QTimer::singleShot(msec: 100, receiver: &o, SLOT(slot())); |
837 | |
838 | const int result = eventLoop.exec(); |
839 | QCOMPARE(result, code); |
840 | } |
841 | |
842 | void tst_QThread::adoptedThreadExec() |
843 | { |
844 | NativeThreadWrapper nativeThread; |
845 | nativeThread.start(functionPointer: adoptedThreadExecFunction); |
846 | nativeThread.join(); |
847 | } |
848 | |
849 | /* |
850 | Test that you get the finished signal when an adopted thread exits. |
851 | */ |
852 | void tst_QThread::adoptedThreadFinished() |
853 | { |
854 | NativeThreadWrapper nativeThread; |
855 | nativeThread.setWaitForStop(); |
856 | nativeThread.startAndWait(); |
857 | |
858 | QObject::connect(sender: nativeThread.qthread, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
859 | |
860 | nativeThread.stop(); |
861 | nativeThread.join(); |
862 | |
863 | QTestEventLoop::instance().enterLoop(secs: 5); |
864 | QVERIFY(!QTestEventLoop::instance().timeout()); |
865 | } |
866 | |
867 | void tst_QThread::adoptedThreadExecFinished() |
868 | { |
869 | NativeThreadWrapper nativeThread; |
870 | nativeThread.setWaitForStop(); |
871 | nativeThread.startAndWait(functionPointer: adoptedThreadExecFunction); |
872 | |
873 | QObject::connect(sender: nativeThread.qthread, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
874 | |
875 | nativeThread.stop(); |
876 | nativeThread.join(); |
877 | |
878 | QTestEventLoop::instance().enterLoop(secs: 5); |
879 | QVERIFY(!QTestEventLoop::instance().timeout()); |
880 | } |
881 | |
882 | void tst_QThread::adoptMultipleThreads() |
883 | { |
884 | #if defined(Q_OS_WIN) |
885 | // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher() |
886 | const int numThreads = 200; |
887 | #else |
888 | const int numThreads = 5; |
889 | #endif |
890 | QVector<NativeThreadWrapper*> nativeThreads; |
891 | |
892 | SignalRecorder recorder; |
893 | |
894 | for (int i = 0; i < numThreads; ++i) { |
895 | nativeThreads.append(t: new NativeThreadWrapper()); |
896 | nativeThreads.at(i)->setWaitForStop(); |
897 | nativeThreads.at(i)->startAndWait(); |
898 | QObject::connect(sender: nativeThreads.at(i)->qthread, SIGNAL(finished()), receiver: &recorder, SLOT(slot())); |
899 | } |
900 | |
901 | QObject::connect(sender: nativeThreads.at(i: numThreads - 1)->qthread, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
902 | |
903 | for (int i = 0; i < numThreads; ++i) { |
904 | nativeThreads.at(i)->stop(); |
905 | nativeThreads.at(i)->join(); |
906 | delete nativeThreads.at(i); |
907 | } |
908 | |
909 | QTestEventLoop::instance().enterLoop(secs: 5); |
910 | QVERIFY(!QTestEventLoop::instance().timeout()); |
911 | QCOMPARE(recorder.activationCount.loadRelaxed(), numThreads); |
912 | } |
913 | |
914 | void tst_QThread::adoptMultipleThreadsOverlap() |
915 | { |
916 | #if defined(Q_OS_WIN) |
917 | // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher() |
918 | const int numThreads = 200; |
919 | #else |
920 | const int numThreads = 5; |
921 | #endif |
922 | QVector<NativeThreadWrapper*> nativeThreads; |
923 | |
924 | SignalRecorder recorder; |
925 | |
926 | for (int i = 0; i < numThreads; ++i) { |
927 | nativeThreads.append(t: new NativeThreadWrapper()); |
928 | nativeThreads.at(i)->setWaitForStop(); |
929 | nativeThreads.at(i)->mutex.lock(); |
930 | nativeThreads.at(i)->start(); |
931 | } |
932 | for (int i = 0; i < numThreads; ++i) { |
933 | nativeThreads.at(i)->startCondition.wait(lockedMutex: &nativeThreads.at(i)->mutex); |
934 | QObject::connect(sender: nativeThreads.at(i)->qthread, SIGNAL(finished()), receiver: &recorder, SLOT(slot())); |
935 | nativeThreads.at(i)->mutex.unlock(); |
936 | } |
937 | |
938 | QObject::connect(sender: nativeThreads.at(i: numThreads - 1)->qthread, SIGNAL(finished()), receiver: &QTestEventLoop::instance(), SLOT(exitLoop())); |
939 | |
940 | for (int i = 0; i < numThreads; ++i) { |
941 | nativeThreads.at(i)->stop(); |
942 | nativeThreads.at(i)->join(); |
943 | delete nativeThreads.at(i); |
944 | } |
945 | |
946 | QTestEventLoop::instance().enterLoop(secs: 5); |
947 | QVERIFY(!QTestEventLoop::instance().timeout()); |
948 | QCOMPARE(recorder.activationCount.loadRelaxed(), numThreads); |
949 | } |
950 | |
951 | // Disconnects on WinCE |
952 | void tst_QThread::stressTest() |
953 | { |
954 | if (EmulationDetector::isRunningArmOnX86()) |
955 | QSKIP("Qemu uses too much memory for each thread. Test would run out of memory." ); |
956 | |
957 | QElapsedTimer timer; |
958 | timer.start(); |
959 | while (timer.elapsed() < one_minute) { |
960 | Current_Thread t; |
961 | t.start(); |
962 | t.wait(time: one_minute); |
963 | } |
964 | } |
965 | |
966 | class Syncronizer : public QObject |
967 | { Q_OBJECT |
968 | public slots: |
969 | void setProp(int p) { |
970 | if(m_prop != p) { |
971 | m_prop = p; |
972 | emit propChanged(p); |
973 | } |
974 | } |
975 | signals: |
976 | void propChanged(int); |
977 | public: |
978 | Syncronizer() : m_prop(42) {} |
979 | int m_prop; |
980 | }; |
981 | |
982 | void tst_QThread::exitAndStart() |
983 | { |
984 | QThread thread; |
985 | thread.exit(retcode: 555); //should do nothing |
986 | |
987 | thread.start(); |
988 | |
989 | //test that the thread is running by executing queued connected signal there |
990 | Syncronizer sync1; |
991 | sync1.moveToThread(thread: &thread); |
992 | Syncronizer sync2; |
993 | sync2.moveToThread(thread: &thread); |
994 | connect(sender: &sync2, SIGNAL(propChanged(int)), receiver: &sync1, SLOT(setProp(int)), Qt::QueuedConnection); |
995 | connect(sender: &sync1, SIGNAL(propChanged(int)), receiver: &thread, SLOT(quit()), Qt::QueuedConnection); |
996 | QMetaObject::invokeMethod(obj: &sync2, member: "setProp" , type: Qt::QueuedConnection , Q_ARG(int, 89)); |
997 | QTRY_VERIFY(thread.wait(10)); |
998 | QCOMPARE(sync2.m_prop, 89); |
999 | QCOMPARE(sync1.m_prop, 89); |
1000 | } |
1001 | |
1002 | void tst_QThread::exitAndExec() |
1003 | { |
1004 | class Thread : public QThread { |
1005 | public: |
1006 | QSemaphore sem1; |
1007 | QSemaphore sem2; |
1008 | volatile int value; |
1009 | void run() { |
1010 | sem1.acquire(); |
1011 | value = exec(); //First entrence |
1012 | sem2.release(); |
1013 | value = exec(); // Second loop |
1014 | } |
1015 | }; |
1016 | Thread thread; |
1017 | thread.value = 0; |
1018 | thread.start(); |
1019 | thread.exit(retcode: 556); |
1020 | thread.sem1.release(); //should exit the first loop |
1021 | thread.sem2.acquire(); |
1022 | int v = thread.value; |
1023 | QCOMPARE(v, 556); |
1024 | |
1025 | //test that the thread is running by executing queued connected signal there |
1026 | Syncronizer sync1; |
1027 | sync1.moveToThread(thread: &thread); |
1028 | Syncronizer sync2; |
1029 | sync2.moveToThread(thread: &thread); |
1030 | connect(sender: &sync2, SIGNAL(propChanged(int)), receiver: &sync1, SLOT(setProp(int)), Qt::QueuedConnection); |
1031 | connect(sender: &sync1, SIGNAL(propChanged(int)), receiver: &thread, SLOT(quit()), Qt::QueuedConnection); |
1032 | QMetaObject::invokeMethod(obj: &sync2, member: "setProp" , type: Qt::QueuedConnection , Q_ARG(int, 89)); |
1033 | QTRY_VERIFY(thread.wait(10)); |
1034 | QCOMPARE(sync2.m_prop, 89); |
1035 | QCOMPARE(sync1.m_prop, 89); |
1036 | } |
1037 | |
1038 | void tst_QThread::connectThreadFinishedSignalToObjectDeleteLaterSlot() |
1039 | { |
1040 | QThread thread; |
1041 | QObject *object = new QObject; |
1042 | QPointer<QObject> p = object; |
1043 | QVERIFY(!p.isNull()); |
1044 | connect(sender: &thread, SIGNAL(started()), receiver: &thread, SLOT(quit()), Qt::DirectConnection); |
1045 | connect(sender: &thread, SIGNAL(finished()), receiver: object, SLOT(deleteLater())); |
1046 | object->moveToThread(thread: &thread); |
1047 | thread.start(); |
1048 | QVERIFY(thread.wait(30000)); |
1049 | QVERIFY(p.isNull()); |
1050 | } |
1051 | |
1052 | class Waiting_Thread : public QThread |
1053 | { |
1054 | public: |
1055 | enum { WaitTime = 800 }; |
1056 | QMutex mutex; |
1057 | QWaitCondition cond1; |
1058 | QWaitCondition cond2; |
1059 | |
1060 | void run() |
1061 | { |
1062 | QMutexLocker locker(&mutex); |
1063 | cond1.wait(lockedMutex: &mutex); |
1064 | cond2.wait(lockedMutex: &mutex, time: WaitTime); |
1065 | } |
1066 | }; |
1067 | |
1068 | void tst_QThread::wait2() |
1069 | { |
1070 | QElapsedTimer timer; |
1071 | Waiting_Thread thread; |
1072 | thread.start(); |
1073 | timer.start(); |
1074 | QVERIFY(!thread.wait(Waiting_Thread::WaitTime)); |
1075 | qint64 elapsed = timer.elapsed(); // On Windows, we sometimes get (WaitTime - 9). |
1076 | QVERIFY2(elapsed >= Waiting_Thread::WaitTime - 10, |
1077 | qPrintable(msgElapsed(elapsed))); |
1078 | |
1079 | timer.start(); |
1080 | thread.cond1.wakeOne(); |
1081 | QVERIFY(thread.wait(/*Waiting_Thread::WaitTime * 1.4*/)); |
1082 | elapsed = timer.elapsed(); |
1083 | QVERIFY2(elapsed - Waiting_Thread::WaitTime >= -1, |
1084 | qPrintable(msgElapsed(elapsed))); |
1085 | } |
1086 | |
1087 | class SlowSlotObject : public QObject |
1088 | { |
1089 | Q_OBJECT |
1090 | public: |
1091 | QMutex mutex; |
1092 | QWaitCondition cond; |
1093 | public slots: |
1094 | void slowSlot() { |
1095 | QMutexLocker locker(&mutex); |
1096 | cond.wait(lockedMutex: &mutex); |
1097 | } |
1098 | }; |
1099 | |
1100 | void tst_QThread::wait3_slowDestructor() |
1101 | { |
1102 | SlowSlotObject slow; |
1103 | QThread thread; |
1104 | QObject::connect(sender: &thread, signal: &QThread::finished, |
1105 | receiver: &slow, slot: &SlowSlotObject::slowSlot, type: Qt::DirectConnection); |
1106 | QElapsedTimer timer; |
1107 | |
1108 | thread.start(); |
1109 | thread.quit(); |
1110 | // Calling quit() will cause the thread to finish and enter the blocking slowSlot(). |
1111 | |
1112 | timer.start(); |
1113 | { |
1114 | // Ensure thread finishes quickly after the checks - regardless of success: |
1115 | const auto wakeSlow = qScopeGuard(f: [&slow]() -> void { slow.cond.wakeOne(); }); |
1116 | QVERIFY(!thread.wait(Waiting_Thread::WaitTime)); |
1117 | const qint64 elapsed = timer.elapsed(); |
1118 | QVERIFY2(elapsed >= Waiting_Thread::WaitTime - 1, |
1119 | qPrintable(QString::fromLatin1("elapsed: %1" ).arg(elapsed))); |
1120 | } |
1121 | QVERIFY(thread.wait(one_minute)); |
1122 | } |
1123 | |
1124 | void tst_QThread::destroyFinishRace() |
1125 | { |
1126 | class Thread : public QThread { void run() {} }; |
1127 | for (int i = 0; i < 15; i++) { |
1128 | Thread *thr = new Thread; |
1129 | connect(sender: thr, SIGNAL(finished()), receiver: thr, SLOT(deleteLater())); |
1130 | QPointer<QThread> weak(static_cast<QThread*>(thr)); |
1131 | thr->start(); |
1132 | while (weak) { |
1133 | qApp->processEvents(); |
1134 | qApp->processEvents(); |
1135 | qApp->processEvents(); |
1136 | qApp->processEvents(); |
1137 | } |
1138 | } |
1139 | } |
1140 | |
1141 | void tst_QThread::startFinishRace() |
1142 | { |
1143 | class Thread : public QThread { |
1144 | public: |
1145 | Thread() : i (50) {} |
1146 | void run() { |
1147 | i--; |
1148 | if (!i) disconnect(sender: this, SIGNAL(finished()), receiver: 0, member: 0); |
1149 | } |
1150 | int i; |
1151 | }; |
1152 | for (int i = 0; i < 15; i++) { |
1153 | Thread thr; |
1154 | connect(sender: &thr, SIGNAL(finished()), receiver: &thr, SLOT(start())); |
1155 | thr.start(); |
1156 | while (!thr.isFinished() || thr.i != 0) { |
1157 | qApp->processEvents(); |
1158 | qApp->processEvents(); |
1159 | qApp->processEvents(); |
1160 | qApp->processEvents(); |
1161 | } |
1162 | QCOMPARE(thr.i, 0); |
1163 | } |
1164 | } |
1165 | |
1166 | void tst_QThread::startAndQuitCustomEventLoop() |
1167 | { |
1168 | struct Thread : QThread { |
1169 | void run() { QEventLoop().exec(); } |
1170 | }; |
1171 | |
1172 | for (int i = 0; i < 5; i++) { |
1173 | Thread t; |
1174 | t.start(); |
1175 | t.quit(); |
1176 | t.wait(); |
1177 | } |
1178 | } |
1179 | |
1180 | class FinishedTestObject : public QObject { |
1181 | Q_OBJECT |
1182 | public: |
1183 | FinishedTestObject() : ok(false) {} |
1184 | bool ok; |
1185 | public slots: |
1186 | void slotFinished() { |
1187 | QThread *t = qobject_cast<QThread *>(object: sender()); |
1188 | ok = t && t->isFinished() && !t->isRunning(); |
1189 | } |
1190 | }; |
1191 | |
1192 | void tst_QThread::isRunningInFinished() |
1193 | { |
1194 | for (int i = 0; i < 15; i++) { |
1195 | QThread thread; |
1196 | thread.start(); |
1197 | FinishedTestObject localObject; |
1198 | FinishedTestObject inThreadObject; |
1199 | localObject.setObjectName("..." ); |
1200 | inThreadObject.moveToThread(thread: &thread); |
1201 | connect(sender: &thread, SIGNAL(finished()), receiver: &localObject, SLOT(slotFinished())); |
1202 | connect(sender: &thread, SIGNAL(finished()), receiver: &inThreadObject, SLOT(slotFinished())); |
1203 | QEventLoop loop; |
1204 | connect(sender: &thread, SIGNAL(finished()), receiver: &loop, SLOT(quit())); |
1205 | QMetaObject::invokeMethod(obj: &thread, member: "quit" , type: Qt::QueuedConnection); |
1206 | loop.exec(); |
1207 | QVERIFY(!thread.isRunning()); |
1208 | QVERIFY(thread.isFinished()); |
1209 | QVERIFY(localObject.ok); |
1210 | QVERIFY(inThreadObject.ok); |
1211 | } |
1212 | } |
1213 | |
1214 | QT_BEGIN_NAMESPACE |
1215 | Q_CORE_EXPORT uint qGlobalPostedEventsCount(); |
1216 | QT_END_NAMESPACE |
1217 | |
1218 | class DummyEventDispatcher : public QAbstractEventDispatcher { |
1219 | public: |
1220 | DummyEventDispatcher() : QAbstractEventDispatcher() {} |
1221 | bool processEvents(QEventLoop::ProcessEventsFlags) { |
1222 | visited.storeRelaxed(newValue: true); |
1223 | emit awake(); |
1224 | QCoreApplication::sendPostedEvents(); |
1225 | return false; |
1226 | } |
1227 | bool hasPendingEvents() { |
1228 | return qGlobalPostedEventsCount(); |
1229 | } |
1230 | void registerSocketNotifier(QSocketNotifier *) {} |
1231 | void unregisterSocketNotifier(QSocketNotifier *) {} |
1232 | void registerTimer(int, int, Qt::TimerType, QObject *) {} |
1233 | bool unregisterTimer(int ) { return false; } |
1234 | bool unregisterTimers(QObject *) { return false; } |
1235 | QList<TimerInfo> registeredTimers(QObject *) const { return QList<TimerInfo>(); } |
1236 | int remainingTime(int) { return 0; } |
1237 | void wakeUp() {} |
1238 | void interrupt() {} |
1239 | void flush() {} |
1240 | |
1241 | #ifdef Q_OS_WIN |
1242 | bool registerEventNotifier(QWinEventNotifier *) { return false; } |
1243 | void unregisterEventNotifier(QWinEventNotifier *) { } |
1244 | #endif |
1245 | |
1246 | QBasicAtomicInt visited; // bool |
1247 | }; |
1248 | |
1249 | class ThreadObj : public QObject |
1250 | { |
1251 | Q_OBJECT |
1252 | public slots: |
1253 | void visit() { |
1254 | emit visited(); |
1255 | } |
1256 | signals: |
1257 | void visited(); |
1258 | }; |
1259 | |
1260 | void tst_QThread::customEventDispatcher() |
1261 | { |
1262 | QThread thr; |
1263 | // there should be no ED yet |
1264 | QVERIFY(!thr.eventDispatcher()); |
1265 | DummyEventDispatcher *ed = new DummyEventDispatcher; |
1266 | thr.setEventDispatcher(ed); |
1267 | // the new ED should be set |
1268 | QCOMPARE(thr.eventDispatcher(), ed); |
1269 | // test the alternative API of QAbstractEventDispatcher |
1270 | QCOMPARE(QAbstractEventDispatcher::instance(&thr), ed); |
1271 | thr.start(); |
1272 | // start() should not overwrite the ED |
1273 | QCOMPARE(thr.eventDispatcher(), ed); |
1274 | |
1275 | ThreadObj obj; |
1276 | obj.moveToThread(thread: &thr); |
1277 | // move was successful? |
1278 | QCOMPARE(obj.thread(), &thr); |
1279 | QEventLoop loop; |
1280 | connect(sender: &obj, SIGNAL(visited()), receiver: &loop, SLOT(quit()), Qt::QueuedConnection); |
1281 | QMetaObject::invokeMethod(obj: &obj, member: "visit" , type: Qt::QueuedConnection); |
1282 | loop.exec(); |
1283 | // test that the ED has really been used |
1284 | QVERIFY(ed->visited.loadRelaxed()); |
1285 | |
1286 | QPointer<DummyEventDispatcher> weak_ed(ed); |
1287 | QVERIFY(!weak_ed.isNull()); |
1288 | thr.quit(); |
1289 | // wait for thread to be stopped |
1290 | QVERIFY(thr.wait(30000)); |
1291 | // test that ED has been deleted |
1292 | QVERIFY(weak_ed.isNull()); |
1293 | } |
1294 | |
1295 | class Job : public QObject |
1296 | { |
1297 | Q_OBJECT |
1298 | public: |
1299 | Job(QThread *thread, int deleteDelay, bool *flag, QObject *parent = 0) |
1300 | : QObject(parent), quitLocker(thread), exitThreadCalled(*flag) |
1301 | { |
1302 | exitThreadCalled = false; |
1303 | moveToThread(thread); |
1304 | QTimer::singleShot(msec: deleteDelay, receiver: this, SLOT(deleteLater())); |
1305 | QTimer::singleShot(msec: 1000, receiver: this, SLOT(exitThread())); |
1306 | } |
1307 | |
1308 | private slots: |
1309 | void exitThread() |
1310 | { |
1311 | exitThreadCalled = true; |
1312 | thread()->exit(retcode: 1); |
1313 | } |
1314 | |
1315 | private: |
1316 | QEventLoopLocker quitLocker; |
1317 | public: |
1318 | bool &exitThreadCalled; |
1319 | }; |
1320 | |
1321 | void tst_QThread::quitLock() |
1322 | { |
1323 | QThread thread; |
1324 | bool exitThreadCalled; |
1325 | |
1326 | QEventLoop loop; |
1327 | connect(sender: &thread, SIGNAL(finished()), receiver: &loop, SLOT(quit())); |
1328 | |
1329 | Job *job; |
1330 | |
1331 | thread.start(); |
1332 | job = new Job(&thread, 500, &exitThreadCalled); |
1333 | QCOMPARE(job->thread(), &thread); |
1334 | loop.exec(); |
1335 | QVERIFY(!exitThreadCalled); |
1336 | |
1337 | thread.start(); |
1338 | job = new Job(&thread, 2500, &exitThreadCalled); |
1339 | QCOMPARE(job->thread(), &thread); |
1340 | loop.exec(); |
1341 | QVERIFY(exitThreadCalled); |
1342 | |
1343 | delete job; |
1344 | } |
1345 | |
1346 | void tst_QThread::create() |
1347 | { |
1348 | #if !QT_CONFIG(cxx11_future) |
1349 | QSKIP("This test requires QThread::create" ); |
1350 | #else |
1351 | { |
1352 | const auto &function = [](){}; |
1353 | QScopedPointer<QThread> thread(QThread::create(f: function)); |
1354 | QVERIFY(thread); |
1355 | QVERIFY(!thread->isRunning()); |
1356 | thread->start(); |
1357 | QVERIFY(thread->wait()); |
1358 | } |
1359 | |
1360 | { |
1361 | // no side effects before starting |
1362 | int i = 0; |
1363 | const auto &function = [&i]() { i = 42; }; |
1364 | QScopedPointer<QThread> thread(QThread::create(f: function)); |
1365 | QVERIFY(thread); |
1366 | QVERIFY(!thread->isRunning()); |
1367 | QCOMPARE(i, 0); |
1368 | thread->start(); |
1369 | QVERIFY(thread->wait()); |
1370 | QCOMPARE(i, 42); |
1371 | } |
1372 | |
1373 | { |
1374 | // control thread progress |
1375 | QSemaphore semaphore1; |
1376 | QSemaphore semaphore2; |
1377 | |
1378 | const auto &function = [&semaphore1, &semaphore2]() -> void |
1379 | { |
1380 | semaphore1.acquire(); |
1381 | semaphore2.release(); |
1382 | }; |
1383 | |
1384 | QScopedPointer<QThread> thread(QThread::create(f: function)); |
1385 | |
1386 | QVERIFY(thread); |
1387 | thread->start(); |
1388 | QTRY_VERIFY(thread->isRunning()); |
1389 | semaphore1.release(); |
1390 | semaphore2.acquire(); |
1391 | QVERIFY(thread->wait()); |
1392 | QVERIFY(!thread->isRunning()); |
1393 | } |
1394 | |
1395 | { |
1396 | // ignore return values |
1397 | const auto &function = []() { return 42; }; |
1398 | QScopedPointer<QThread> thread(QThread::create(f: function)); |
1399 | QVERIFY(thread); |
1400 | QVERIFY(!thread->isRunning()); |
1401 | thread->start(); |
1402 | QVERIFY(thread->wait()); |
1403 | } |
1404 | |
1405 | { |
1406 | // return value of create |
1407 | QScopedPointer<QThread> thread; |
1408 | QSemaphore s; |
1409 | const auto &function = [&thread, &s]() -> void |
1410 | { |
1411 | s.acquire(); |
1412 | QCOMPARE(thread.data(), QThread::currentThread()); |
1413 | }; |
1414 | |
1415 | thread.reset(other: QThread::create(f: function)); |
1416 | QVERIFY(thread); |
1417 | thread->start(); |
1418 | QTRY_VERIFY(thread->isRunning()); |
1419 | s.release(); |
1420 | QVERIFY(thread->wait()); |
1421 | } |
1422 | |
1423 | { |
1424 | // move-only parameters |
1425 | struct MoveOnlyValue { |
1426 | explicit MoveOnlyValue(int v) : v(v) {} |
1427 | ~MoveOnlyValue() = default; |
1428 | MoveOnlyValue(const MoveOnlyValue &) = delete; |
1429 | MoveOnlyValue(MoveOnlyValue &&) = default; |
1430 | MoveOnlyValue &operator=(const MoveOnlyValue &) = delete; |
1431 | MoveOnlyValue &operator=(MoveOnlyValue &&) = default; |
1432 | int v; |
1433 | }; |
1434 | |
1435 | struct MoveOnlyFunctor { |
1436 | explicit MoveOnlyFunctor(int *i) : i(i) {} |
1437 | ~MoveOnlyFunctor() = default; |
1438 | MoveOnlyFunctor(const MoveOnlyFunctor &) = delete; |
1439 | MoveOnlyFunctor(MoveOnlyFunctor &&) = default; |
1440 | MoveOnlyFunctor &operator=(const MoveOnlyFunctor &) = delete; |
1441 | MoveOnlyFunctor &operator=(MoveOnlyFunctor &&) = default; |
1442 | int operator()() { return (*i = 42); } |
1443 | int *i; |
1444 | }; |
1445 | |
1446 | { |
1447 | int i = 0; |
1448 | MoveOnlyFunctor f(&i); |
1449 | QScopedPointer<QThread> thread(QThread::create(f: std::move(f))); |
1450 | QVERIFY(thread); |
1451 | QVERIFY(!thread->isRunning()); |
1452 | thread->start(); |
1453 | QVERIFY(thread->wait()); |
1454 | QCOMPARE(i, 42); |
1455 | } |
1456 | |
1457 | #if defined(__cpp_init_captures) && __cpp_init_captures >= 201304 |
1458 | { |
1459 | int i = 0; |
1460 | MoveOnlyValue mo(123); |
1461 | auto moveOnlyFunction = [&i, mo = std::move(mo)]() { i = mo.v; }; |
1462 | QScopedPointer<QThread> thread(QThread::create(f: std::move(moveOnlyFunction))); |
1463 | QVERIFY(thread); |
1464 | QVERIFY(!thread->isRunning()); |
1465 | thread->start(); |
1466 | QVERIFY(thread->wait()); |
1467 | QCOMPARE(i, 123); |
1468 | } |
1469 | #endif // __cpp_init_captures |
1470 | |
1471 | #ifdef QTHREAD_HAS_VARIADIC_CREATE |
1472 | { |
1473 | int i = 0; |
1474 | const auto &function = [&i](MoveOnlyValue &&mo) { i = mo.v; }; |
1475 | QScopedPointer<QThread> thread(QThread::create(f: function, args: MoveOnlyValue(123))); |
1476 | QVERIFY(thread); |
1477 | QVERIFY(!thread->isRunning()); |
1478 | thread->start(); |
1479 | QVERIFY(thread->wait()); |
1480 | QCOMPARE(i, 123); |
1481 | } |
1482 | |
1483 | { |
1484 | int i = 0; |
1485 | const auto &function = [&i](MoveOnlyValue &&mo) { i = mo.v; }; |
1486 | MoveOnlyValue mo(-1); |
1487 | QScopedPointer<QThread> thread(QThread::create(f: function, args: std::move(mo))); |
1488 | QVERIFY(thread); |
1489 | QVERIFY(!thread->isRunning()); |
1490 | thread->start(); |
1491 | QVERIFY(thread->wait()); |
1492 | QCOMPARE(i, -1); |
1493 | } |
1494 | #endif // QTHREAD_HAS_VARIADIC_CREATE |
1495 | } |
1496 | |
1497 | #ifdef QTHREAD_HAS_VARIADIC_CREATE |
1498 | { |
1499 | // simple parameter passing |
1500 | int i = 0; |
1501 | const auto &function = [&i](int j, int k) { i = j * k; }; |
1502 | QScopedPointer<QThread> thread(QThread::create(f: function, args: 3, args: 4)); |
1503 | QVERIFY(thread); |
1504 | QVERIFY(!thread->isRunning()); |
1505 | QCOMPARE(i, 0); |
1506 | thread->start(); |
1507 | QVERIFY(thread->wait()); |
1508 | QCOMPARE(i, 12); |
1509 | } |
1510 | |
1511 | { |
1512 | // ignore return values (with parameters) |
1513 | const auto &function = [](double d) { return d * 2.0; }; |
1514 | QScopedPointer<QThread> thread(QThread::create(f: function, args: 3.14)); |
1515 | QVERIFY(thread); |
1516 | QVERIFY(!thread->isRunning()); |
1517 | thread->start(); |
1518 | QVERIFY(thread->wait()); |
1519 | } |
1520 | |
1521 | { |
1522 | // handling of pointers to member functions, std::ref, etc. |
1523 | struct S { |
1524 | S() : v(0) {} |
1525 | void doSomething() { ++v; } |
1526 | int v; |
1527 | }; |
1528 | |
1529 | S object; |
1530 | |
1531 | QCOMPARE(object.v, 0); |
1532 | |
1533 | QScopedPointer<QThread> thread; |
1534 | thread.reset(other: QThread::create(f: &S::doSomething, args&: object)); |
1535 | QVERIFY(thread); |
1536 | QVERIFY(!thread->isRunning()); |
1537 | thread->start(); |
1538 | QVERIFY(thread->wait()); |
1539 | |
1540 | QCOMPARE(object.v, 0); // a copy was passed, this should still be 0 |
1541 | |
1542 | thread.reset(other: QThread::create(f: &S::doSomething, args: std::ref(t&: object))); |
1543 | QVERIFY(thread); |
1544 | QVERIFY(!thread->isRunning()); |
1545 | thread->start(); |
1546 | QVERIFY(thread->wait()); |
1547 | |
1548 | QCOMPARE(object.v, 1); |
1549 | |
1550 | thread.reset(other: QThread::create(f: &S::doSomething, args: &object)); |
1551 | QVERIFY(thread); |
1552 | QVERIFY(!thread->isRunning()); |
1553 | thread->start(); |
1554 | QVERIFY(thread->wait()); |
1555 | |
1556 | QCOMPARE(object.v, 2); |
1557 | } |
1558 | |
1559 | { |
1560 | // std::ref into ordinary reference |
1561 | int i = 42; |
1562 | const auto &function = [](int &i) { i *= 2; }; |
1563 | QScopedPointer<QThread> thread(QThread::create(f: function, args: std::ref(t&: i))); |
1564 | QVERIFY(thread); |
1565 | thread->start(); |
1566 | QVERIFY(thread->wait()); |
1567 | QCOMPARE(i, 84); |
1568 | } |
1569 | |
1570 | #ifndef QT_NO_EXCEPTIONS |
1571 | { |
1572 | // exceptions when copying/decaying the arguments are thrown at build side and won't terminate |
1573 | class ThreadException : public std::exception |
1574 | { |
1575 | }; |
1576 | |
1577 | struct ThrowWhenCopying |
1578 | { |
1579 | ThrowWhenCopying() = default; |
1580 | ThrowWhenCopying(const ThrowWhenCopying &) |
1581 | { |
1582 | throw ThreadException(); |
1583 | } |
1584 | ~ThrowWhenCopying() = default; |
1585 | ThrowWhenCopying &operator=(const ThrowWhenCopying &) = default; |
1586 | }; |
1587 | |
1588 | const auto &function = [](const ThrowWhenCopying &){}; |
1589 | QScopedPointer<QThread> thread; |
1590 | ThrowWhenCopying t; |
1591 | QVERIFY_EXCEPTION_THROWN(thread.reset(QThread::create(function, t)), ThreadException); |
1592 | QVERIFY(!thread); |
1593 | } |
1594 | #endif // QT_NO_EXCEPTIONS |
1595 | #endif // QTHREAD_HAS_VARIADIC_CREATE |
1596 | #endif // QT_CONFIG(cxx11_future) |
1597 | } |
1598 | |
1599 | class StopableJob : public QObject |
1600 | { |
1601 | Q_OBJECT |
1602 | public: |
1603 | StopableJob (QSemaphore &sem) : sem(sem) {} |
1604 | QSemaphore &sem; |
1605 | public Q_SLOTS: |
1606 | void run() { |
1607 | sem.release(); |
1608 | while (!thread()->isInterruptionRequested()) |
1609 | QTest::qSleep(ms: 10); |
1610 | sem.release(); |
1611 | Q_EMIT finished(); |
1612 | } |
1613 | Q_SIGNALS: |
1614 | void finished(); |
1615 | }; |
1616 | |
1617 | void tst_QThread::requestTermination() |
1618 | { |
1619 | QThread thread; |
1620 | QVERIFY(!thread.isInterruptionRequested()); |
1621 | QSemaphore sem; |
1622 | StopableJob *j = new StopableJob(sem); |
1623 | j->moveToThread(thread: &thread); |
1624 | connect(sender: &thread, signal: &QThread::started, receiver: j, slot: &StopableJob::run); |
1625 | connect(sender: j, signal: &StopableJob::finished, receiver: &thread, slot: &QThread::quit, type: Qt::DirectConnection); |
1626 | connect(sender: &thread, signal: &QThread::finished, receiver: j, slot: &QObject::deleteLater); |
1627 | thread.start(); |
1628 | QVERIFY(!thread.isInterruptionRequested()); |
1629 | sem.acquire(); |
1630 | QVERIFY(!thread.wait(1000)); |
1631 | thread.requestInterruption(); |
1632 | sem.acquire(); |
1633 | QVERIFY(thread.wait(1000)); |
1634 | QVERIFY(!thread.isInterruptionRequested()); |
1635 | } |
1636 | |
1637 | /* |
1638 | This is a regression test for QTBUG-96846. |
1639 | |
1640 | Incorrect system thread ID cleanup can cause QThread::wait() to report that |
1641 | a thread is trying to wait for itself. |
1642 | */ |
1643 | void tst_QThread::threadIdReuse() |
1644 | { |
1645 | class Thread1 : public QThread { |
1646 | public: |
1647 | // It's important that those thread ID's are not accessed concurrently |
1648 | Qt::HANDLE savedThreadId; |
1649 | |
1650 | void run() override { savedThreadId = QThread::currentThreadId(); } |
1651 | }; |
1652 | |
1653 | class Thread2 : public Thread1 { |
1654 | public: |
1655 | bool waitOk; |
1656 | Thread2(QThread *otherThread) : Thread1(), waitOk(false), otherThread(otherThread) {} |
1657 | |
1658 | void run() override { |
1659 | Thread1::run(); |
1660 | waitOk = otherThread->wait(); |
1661 | } |
1662 | |
1663 | private: |
1664 | QThread *const otherThread; |
1665 | }; |
1666 | |
1667 | Thread1 thread1; |
1668 | thread1.start(); |
1669 | QVERIFY(thread1.wait()); |
1670 | |
1671 | // If the system thread allocated for thread1 is destroyed before thread2 is |
1672 | // started, at least on some versions of Linux the system thread ID for |
1673 | // thread2 would be the same as one that was used for thread1. |
1674 | |
1675 | // The system thread may be alive for some time after returning from |
1676 | // QThread::wait() because the implementation is using detachable threads, so |
1677 | // some additional time is required for the system thread to terminate. Not |
1678 | // waiting long enough here would result in a new system thread ID being |
1679 | // allocated for thread2 and this test passing even without a fix for |
1680 | // QTBUG-96846. |
1681 | bool threadIdReused = false; |
1682 | |
1683 | for (int i = 0; i < 42; i++) { |
1684 | QThread::msleep(1); |
1685 | |
1686 | Thread2 thread2(&thread1); |
1687 | thread2.start(); |
1688 | QVERIFY(thread2.wait()); |
1689 | QVERIFY(thread2.waitOk); |
1690 | |
1691 | if (thread1.savedThreadId == thread2.savedThreadId) { |
1692 | qDebug(msg: "Thread ID reused at iteration %d" , i); |
1693 | threadIdReused = true; |
1694 | break; |
1695 | } |
1696 | } |
1697 | |
1698 | if (!threadIdReused) { |
1699 | QSKIP("Thread ID was not reused" ); |
1700 | } |
1701 | } |
1702 | |
1703 | QTEST_MAIN(tst_QThread) |
1704 | #include "tst_qthread.moc" |
1705 | |