| 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 <qabstracteventdispatcher.h> |
| 32 | #include <qcoreapplication.h> |
| 33 | #include <qcoreevent.h> |
| 34 | #include <qeventloop.h> |
| 35 | #include <private/qeventloop_p.h> |
| 36 | #if defined(Q_OS_UNIX) |
| 37 | #include <private/qeventdispatcher_unix_p.h> |
| 38 | #include <QtCore/private/qcore_unix_p.h> |
| 39 | #if defined(HAVE_GLIB) |
| 40 | #include <private/qeventdispatcher_glib_p.h> |
| 41 | #endif |
| 42 | #endif |
| 43 | #include <qmutex.h> |
| 44 | #include <qthread.h> |
| 45 | #include <qtimer.h> |
| 46 | #include <qwaitcondition.h> |
| 47 | #include <QTcpServer> |
| 48 | #include <QTcpSocket> |
| 49 | |
| 50 | class EventLoopExiter : public QObject |
| 51 | { |
| 52 | Q_OBJECT |
| 53 | QEventLoop *eventLoop; |
| 54 | public: |
| 55 | inline EventLoopExiter(QEventLoop *el) |
| 56 | : eventLoop(el) |
| 57 | { } |
| 58 | public slots: |
| 59 | void exit(); |
| 60 | void exit1(); |
| 61 | void exit2(); |
| 62 | }; |
| 63 | |
| 64 | void EventLoopExiter::exit() |
| 65 | { eventLoop->exit(); } |
| 66 | |
| 67 | void EventLoopExiter::exit1() |
| 68 | { eventLoop->exit(returnCode: 1); } |
| 69 | |
| 70 | void EventLoopExiter::exit2() |
| 71 | { eventLoop->exit(returnCode: 2); } |
| 72 | |
| 73 | class EventLoopThread : public QThread |
| 74 | { |
| 75 | Q_OBJECT |
| 76 | signals: |
| 77 | void checkPoint(); |
| 78 | public: |
| 79 | QEventLoop *eventLoop; |
| 80 | void run(); |
| 81 | }; |
| 82 | |
| 83 | void EventLoopThread::run() |
| 84 | { |
| 85 | eventLoop = new QEventLoop; |
| 86 | emit checkPoint(); |
| 87 | (void) eventLoop->exec(); |
| 88 | delete eventLoop; |
| 89 | eventLoop = 0; |
| 90 | } |
| 91 | |
| 92 | class MultipleExecThread : public QThread |
| 93 | { |
| 94 | Q_OBJECT |
| 95 | signals: |
| 96 | void checkPoint(); |
| 97 | public: |
| 98 | QMutex mutex; |
| 99 | QWaitCondition cond; |
| 100 | volatile int result1; |
| 101 | volatile int result2; |
| 102 | MultipleExecThread() : result1(0xdead), result2(0xbeef) {} |
| 103 | |
| 104 | void run() |
| 105 | { |
| 106 | QMutexLocker locker(&mutex); |
| 107 | // this exec should work |
| 108 | |
| 109 | cond.wakeOne(); |
| 110 | cond.wait(lockedMutex: &mutex); |
| 111 | |
| 112 | QTimer timer; |
| 113 | connect(asender: &timer, SIGNAL(timeout()), SLOT(quit()), atype: Qt::DirectConnection); |
| 114 | timer.setInterval(1000); |
| 115 | timer.start(); |
| 116 | result1 = exec(); |
| 117 | |
| 118 | // this should return immediately, since exit() has been called |
| 119 | cond.wakeOne(); |
| 120 | cond.wait(lockedMutex: &mutex); |
| 121 | QEventLoop eventLoop; |
| 122 | result2 = eventLoop.exec(); |
| 123 | } |
| 124 | }; |
| 125 | |
| 126 | class StartStopEvent: public QEvent |
| 127 | { |
| 128 | public: |
| 129 | explicit StartStopEvent(int type, QEventLoop *loop = 0) |
| 130 | : QEvent(Type(type)), el(loop) |
| 131 | { } |
| 132 | |
| 133 | QEventLoop *el; |
| 134 | }; |
| 135 | |
| 136 | class EventLoopExecutor : public QObject |
| 137 | { |
| 138 | Q_OBJECT |
| 139 | QEventLoop *eventLoop; |
| 140 | public: |
| 141 | int returnCode; |
| 142 | EventLoopExecutor(QEventLoop *eventLoop) |
| 143 | : QObject(), eventLoop(eventLoop), returnCode(-42) |
| 144 | { |
| 145 | } |
| 146 | public slots: |
| 147 | void exec() |
| 148 | { |
| 149 | QTimer::singleShot(msec: 100, receiver: eventLoop, SLOT(quit())); |
| 150 | // this should return immediately, and the timer event should be delivered to |
| 151 | // tst_QEventLoop::exec() test, letting the test complete |
| 152 | returnCode = eventLoop->exec(); |
| 153 | } |
| 154 | }; |
| 155 | |
| 156 | #ifdef QT_GUI_LIB |
| 157 | #define tst_QEventLoop tst_QGuiEventLoop |
| 158 | #endif |
| 159 | |
| 160 | class tst_QEventLoop : public QObject |
| 161 | { |
| 162 | Q_OBJECT |
| 163 | private slots: |
| 164 | // This test *must* run first. See the definition for why. |
| 165 | void processEvents(); |
| 166 | void exec(); |
| 167 | void reexec(); |
| 168 | void execAfterExit(); |
| 169 | void wakeUp(); |
| 170 | void quit(); |
| 171 | #if defined(Q_OS_UNIX) |
| 172 | void processEventsExcludeSocket(); |
| 173 | #endif |
| 174 | void processEventsExcludeTimers(); |
| 175 | void deliverInDefinedOrder(); |
| 176 | |
| 177 | // keep this test last: |
| 178 | void nestedLoops(); |
| 179 | |
| 180 | void testQuitLock(); |
| 181 | |
| 182 | protected: |
| 183 | void customEvent(QEvent *e); |
| 184 | }; |
| 185 | |
| 186 | void tst_QEventLoop::processEvents() |
| 187 | { |
| 188 | QSignalSpy aboutToBlockSpy(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock); |
| 189 | QSignalSpy awakeSpy(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::awake); |
| 190 | |
| 191 | QVERIFY(aboutToBlockSpy.isValid()); |
| 192 | QVERIFY(awakeSpy.isValid()); |
| 193 | |
| 194 | QEventLoop eventLoop; |
| 195 | |
| 196 | QCoreApplication::postEvent(receiver: &eventLoop, event: new QEvent(QEvent::User)); |
| 197 | |
| 198 | // process posted events, QEventLoop::processEvents() should return |
| 199 | // true |
| 200 | QVERIFY(eventLoop.processEvents()); |
| 201 | QCOMPARE(aboutToBlockSpy.count(), 0); |
| 202 | QCOMPARE(awakeSpy.count(), 1); |
| 203 | |
| 204 | // allow any session manager to complete its handshake, so that |
| 205 | // there are no pending events left. This tests that we are able |
| 206 | // to process all events from the queue, otherwise it will hang. |
| 207 | while (eventLoop.processEvents()) |
| 208 | ; |
| 209 | |
| 210 | // make sure the test doesn't block forever |
| 211 | int timerId = startTimer(interval: 100); |
| 212 | |
| 213 | // wait for more events to process, QEventLoop::processEvents() |
| 214 | // should return true |
| 215 | aboutToBlockSpy.clear(); |
| 216 | awakeSpy.clear(); |
| 217 | QVERIFY(eventLoop.processEvents(QEventLoop::WaitForMoreEvents)); |
| 218 | |
| 219 | // We should get one awake for each aboutToBlock, plus one awake when |
| 220 | // processEvents is entered. There is no guarantee that that the |
| 221 | // processEvents call actually blocked, since the OS may introduce |
| 222 | // native events at any time. |
| 223 | QVERIFY(awakeSpy.count() > 0); |
| 224 | QVERIFY(awakeSpy.count() >= aboutToBlockSpy.count()); |
| 225 | |
| 226 | killTimer(id: timerId); |
| 227 | } |
| 228 | |
| 229 | #define EXEC_TIMEOUT 100 |
| 230 | |
| 231 | void tst_QEventLoop::exec() |
| 232 | { |
| 233 | { |
| 234 | QEventLoop eventLoop; |
| 235 | EventLoopExiter exiter(&eventLoop); |
| 236 | int returnCode; |
| 237 | |
| 238 | QTimer::singleShot(EXEC_TIMEOUT, receiver: &exiter, SLOT(exit())); |
| 239 | returnCode = eventLoop.exec(); |
| 240 | QCOMPARE(returnCode, 0); |
| 241 | |
| 242 | QTimer::singleShot(EXEC_TIMEOUT, receiver: &exiter, SLOT(exit1())); |
| 243 | returnCode = eventLoop.exec(); |
| 244 | QCOMPARE(returnCode, 1); |
| 245 | |
| 246 | QTimer::singleShot(EXEC_TIMEOUT, receiver: &exiter, SLOT(exit2())); |
| 247 | returnCode = eventLoop.exec(); |
| 248 | QCOMPARE(returnCode, 2); |
| 249 | } |
| 250 | |
| 251 | { |
| 252 | // calling QEventLoop::exec() after a thread loop has exit()ed should return immediately |
| 253 | // Note: this behaviour differs from QCoreApplication and QEventLoop |
| 254 | // see tst_QCoreApplication::eventLoopExecAfterExit, tst_QEventLoop::reexec |
| 255 | MultipleExecThread thread; |
| 256 | |
| 257 | // start thread and wait for checkpoint |
| 258 | thread.mutex.lock(); |
| 259 | thread.start(); |
| 260 | thread.cond.wait(lockedMutex: &thread.mutex); |
| 261 | |
| 262 | // make sure the eventloop runs |
| 263 | QSignalSpy spy(QAbstractEventDispatcher::instance(thread: &thread), &QAbstractEventDispatcher::awake); |
| 264 | QVERIFY(spy.isValid()); |
| 265 | thread.cond.wakeOne(); |
| 266 | thread.cond.wait(lockedMutex: &thread.mutex); |
| 267 | QVERIFY(spy.count() > 0); |
| 268 | int v = thread.result1; |
| 269 | QCOMPARE(v, 0); |
| 270 | |
| 271 | // exec should return immediately |
| 272 | spy.clear(); |
| 273 | thread.cond.wakeOne(); |
| 274 | thread.mutex.unlock(); |
| 275 | thread.wait(); |
| 276 | QCOMPARE(spy.count(), 0); |
| 277 | v = thread.result2; |
| 278 | QCOMPARE(v, -1); |
| 279 | } |
| 280 | |
| 281 | { |
| 282 | // a single instance of QEventLoop should not be allowed to recurse into exec() |
| 283 | QEventLoop eventLoop; |
| 284 | EventLoopExecutor executor(&eventLoop); |
| 285 | |
| 286 | QTimer::singleShot(EXEC_TIMEOUT, receiver: &executor, SLOT(exec())); |
| 287 | int returnCode = eventLoop.exec(); |
| 288 | QCOMPARE(returnCode, 0); |
| 289 | QCOMPARE(executor.returnCode, -1); |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | void tst_QEventLoop::reexec() |
| 294 | { |
| 295 | QEventLoop loop; |
| 296 | |
| 297 | // exec once |
| 298 | QMetaObject::invokeMethod(obj: &loop, member: "quit" , type: Qt::QueuedConnection); |
| 299 | QCOMPARE(loop.exec(), 0); |
| 300 | |
| 301 | // and again |
| 302 | QMetaObject::invokeMethod(obj: &loop, member: "quit" , type: Qt::QueuedConnection); |
| 303 | QCOMPARE(loop.exec(), 0); |
| 304 | } |
| 305 | |
| 306 | void tst_QEventLoop::execAfterExit() |
| 307 | { |
| 308 | QEventLoop loop; |
| 309 | EventLoopExiter obj(&loop); |
| 310 | |
| 311 | QMetaObject::invokeMethod(obj: &obj, member: "exit" , type: Qt::QueuedConnection); |
| 312 | loop.exit(returnCode: 1); |
| 313 | QCOMPARE(loop.exec(), 0); |
| 314 | } |
| 315 | |
| 316 | void tst_QEventLoop::wakeUp() |
| 317 | { |
| 318 | EventLoopThread thread; |
| 319 | QEventLoop eventLoop; |
| 320 | connect(sender: &thread, SIGNAL(checkPoint()), receiver: &eventLoop, SLOT(quit())); |
| 321 | connect(sender: &thread, SIGNAL(finished()), receiver: &eventLoop, SLOT(quit())); |
| 322 | |
| 323 | thread.start(); |
| 324 | (void) eventLoop.exec(); |
| 325 | |
| 326 | QSignalSpy spy(QAbstractEventDispatcher::instance(thread: &thread), &QAbstractEventDispatcher::awake); |
| 327 | QVERIFY(spy.isValid()); |
| 328 | thread.eventLoop->wakeUp(); |
| 329 | |
| 330 | // give the thread time to wake up |
| 331 | QTimer::singleShot(msec: 1000, receiver: &eventLoop, SLOT(quit())); |
| 332 | (void) eventLoop.exec(); |
| 333 | |
| 334 | QVERIFY(spy.count() > 0); |
| 335 | |
| 336 | thread.quit(); |
| 337 | (void) eventLoop.exec(); |
| 338 | } |
| 339 | |
| 340 | void tst_QEventLoop::quit() |
| 341 | { |
| 342 | QEventLoop eventLoop; |
| 343 | int returnCode; |
| 344 | |
| 345 | QTimer::singleShot(msec: 100, receiver: &eventLoop, SLOT(quit())); |
| 346 | returnCode = eventLoop.exec(); |
| 347 | QCOMPARE(returnCode, 0); |
| 348 | } |
| 349 | |
| 350 | |
| 351 | void tst_QEventLoop::nestedLoops() |
| 352 | { |
| 353 | QCoreApplication::postEvent(receiver: this, event: new StartStopEvent(QEvent::User)); |
| 354 | QCoreApplication::postEvent(receiver: this, event: new StartStopEvent(QEvent::User)); |
| 355 | QCoreApplication::postEvent(receiver: this, event: new StartStopEvent(QEvent::User)); |
| 356 | |
| 357 | // without the fix, this will *wedge* and never return |
| 358 | QTest::qWait(ms: 1000); |
| 359 | } |
| 360 | |
| 361 | void tst_QEventLoop::customEvent(QEvent *e) |
| 362 | { |
| 363 | if (e->type() == QEvent::User) { |
| 364 | QEventLoop loop; |
| 365 | QCoreApplication::postEvent(receiver: this, event: new StartStopEvent(int(QEvent::User) + 1, &loop)); |
| 366 | loop.exec(); |
| 367 | } else { |
| 368 | static_cast<StartStopEvent *>(e)->el->exit(); |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | #if defined(Q_OS_UNIX) |
| 373 | class SocketEventsTester: public QObject |
| 374 | { |
| 375 | Q_OBJECT |
| 376 | public: |
| 377 | SocketEventsTester() |
| 378 | { |
| 379 | socket = 0; |
| 380 | server = 0; |
| 381 | dataSent = false; |
| 382 | dataReadable = false; |
| 383 | testResult = false; |
| 384 | dataArrived = false; |
| 385 | } |
| 386 | ~SocketEventsTester() |
| 387 | { |
| 388 | delete socket; |
| 389 | delete server; |
| 390 | } |
| 391 | bool init() |
| 392 | { |
| 393 | bool ret = false; |
| 394 | server = new QTcpServer(); |
| 395 | socket = new QTcpSocket(); |
| 396 | connect(sender: server, SIGNAL(newConnection()), receiver: this, SLOT(sendHello())); |
| 397 | connect(sender: socket, SIGNAL(readyRead()), receiver: this, SLOT(sendAck()), Qt::DirectConnection); |
| 398 | if((ret = server->listen(address: QHostAddress::LocalHost, port: 0))) { |
| 399 | socket->connectToHost(address: server->serverAddress(), port: server->serverPort()); |
| 400 | socket->waitForConnected(); |
| 401 | } |
| 402 | return ret; |
| 403 | } |
| 404 | |
| 405 | QTcpSocket *socket; |
| 406 | QTcpServer *server; |
| 407 | bool dataSent; |
| 408 | bool dataReadable; |
| 409 | bool testResult; |
| 410 | bool dataArrived; |
| 411 | public slots: |
| 412 | void sendAck() |
| 413 | { |
| 414 | dataArrived = true; |
| 415 | } |
| 416 | void sendHello() |
| 417 | { |
| 418 | char data[10] ="HELLO" ; |
| 419 | qint64 size = sizeof(data); |
| 420 | |
| 421 | QTcpSocket *serverSocket = server->nextPendingConnection(); |
| 422 | QCoreApplication::processEvents(); |
| 423 | serverSocket->write(data, len: size); |
| 424 | dataSent = serverSocket->waitForBytesWritten(msecs: -1); |
| 425 | |
| 426 | if (dataSent) { |
| 427 | pollfd pfd = qt_make_pollfd(fd: socket->socketDescriptor(), POLLIN); |
| 428 | dataReadable = (1 == qt_safe_poll(fds: &pfd, nfds: 1, timeout_ts: nullptr)); |
| 429 | } |
| 430 | |
| 431 | if (!dataReadable) { |
| 432 | testResult = dataArrived; |
| 433 | } else { |
| 434 | QCoreApplication::processEvents(flags: QEventLoop::ExcludeSocketNotifiers); |
| 435 | testResult = dataArrived; |
| 436 | // to check if the deferred event is processed |
| 437 | QCoreApplication::processEvents(); |
| 438 | } |
| 439 | serverSocket->close(); |
| 440 | QThread::currentThread()->exit(retcode: 0); |
| 441 | } |
| 442 | }; |
| 443 | |
| 444 | class SocketTestThread : public QThread |
| 445 | { |
| 446 | Q_OBJECT |
| 447 | public: |
| 448 | SocketTestThread():QThread(0),testResult(false){}; |
| 449 | void run() |
| 450 | { |
| 451 | SocketEventsTester *tester = new SocketEventsTester(); |
| 452 | if (tester->init()) |
| 453 | exec(); |
| 454 | dataSent = tester->dataSent; |
| 455 | dataReadable = tester->dataReadable; |
| 456 | testResult = tester->testResult; |
| 457 | dataArrived = tester->dataArrived; |
| 458 | delete tester; |
| 459 | } |
| 460 | bool dataSent; |
| 461 | bool dataReadable; |
| 462 | bool testResult; |
| 463 | bool dataArrived; |
| 464 | }; |
| 465 | |
| 466 | void tst_QEventLoop::processEventsExcludeSocket() |
| 467 | { |
| 468 | SocketTestThread thread; |
| 469 | thread.start(); |
| 470 | QVERIFY(thread.wait()); |
| 471 | QVERIFY(thread.dataSent); |
| 472 | QVERIFY(thread.dataReadable); |
| 473 | #if defined(HAVE_GLIB) |
| 474 | QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); |
| 475 | if (qobject_cast<QEventDispatcherGlib *>(object: eventDispatcher)) |
| 476 | QEXPECT_FAIL("" , "ExcludeSocketNotifiers is currently broken in the Glib dispatchers" , Continue); |
| 477 | #endif |
| 478 | QVERIFY(!thread.testResult); |
| 479 | QVERIFY(thread.dataArrived); |
| 480 | } |
| 481 | #endif |
| 482 | |
| 483 | class TimerReceiver : public QObject |
| 484 | { |
| 485 | public: |
| 486 | int gotTimerEvent; |
| 487 | |
| 488 | TimerReceiver() |
| 489 | : QObject(), gotTimerEvent(-1) |
| 490 | { } |
| 491 | |
| 492 | void timerEvent(QTimerEvent *event) |
| 493 | { |
| 494 | gotTimerEvent = event->timerId(); |
| 495 | } |
| 496 | }; |
| 497 | |
| 498 | void tst_QEventLoop::processEventsExcludeTimers() |
| 499 | { |
| 500 | TimerReceiver timerReceiver; |
| 501 | int timerId = timerReceiver.startTimer(interval: 0); |
| 502 | |
| 503 | QEventLoop eventLoop; |
| 504 | |
| 505 | // normal process events will send timers |
| 506 | eventLoop.processEvents(); |
| 507 | QCOMPARE(timerReceiver.gotTimerEvent, timerId); |
| 508 | timerReceiver.gotTimerEvent = -1; |
| 509 | |
| 510 | // but not if we exclude timers |
| 511 | eventLoop.processEvents(flags: QEventLoop::X11ExcludeTimers); |
| 512 | |
| 513 | #if defined(Q_OS_UNIX) |
| 514 | QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); |
| 515 | if (!qobject_cast<QEventDispatcherUNIX *>(object: eventDispatcher) |
| 516 | #if defined(HAVE_GLIB) |
| 517 | && !qobject_cast<QEventDispatcherGlib *>(object: eventDispatcher) |
| 518 | #endif |
| 519 | ) |
| 520 | #endif |
| 521 | QEXPECT_FAIL("" , "X11ExcludeTimers only supported in the UNIX/Glib dispatchers" , Continue); |
| 522 | |
| 523 | QCOMPARE(timerReceiver.gotTimerEvent, -1); |
| 524 | timerReceiver.gotTimerEvent = -1; |
| 525 | |
| 526 | // resume timer processing |
| 527 | eventLoop.processEvents(); |
| 528 | QCOMPARE(timerReceiver.gotTimerEvent, timerId); |
| 529 | timerReceiver.gotTimerEvent = -1; |
| 530 | } |
| 531 | |
| 532 | namespace DeliverInDefinedOrder { |
| 533 | enum { NbThread = 3, NbObject = 500, NbEventQueue = 5, NbEvent = 50 }; |
| 534 | |
| 535 | struct CustomEvent : public QEvent { |
| 536 | CustomEvent(int q, int v) : QEvent(Type(User + q)), value(v) {} |
| 537 | int value; |
| 538 | }; |
| 539 | |
| 540 | struct Object : public QObject { |
| 541 | Q_OBJECT |
| 542 | public: |
| 543 | Object() : count(0) { |
| 544 | for (int i = 0; i < NbEventQueue; i++) |
| 545 | lastReceived[i] = -1; |
| 546 | } |
| 547 | int lastReceived[NbEventQueue]; |
| 548 | int count; |
| 549 | virtual void customEvent(QEvent* e) { |
| 550 | QVERIFY(e->type() >= QEvent::User); |
| 551 | QVERIFY(e->type() < QEvent::User + 5); |
| 552 | uint idx = e->type() - QEvent::User; |
| 553 | int value = static_cast<CustomEvent *>(e)->value; |
| 554 | QVERIFY(lastReceived[idx] < value); |
| 555 | lastReceived[idx] = value; |
| 556 | count++; |
| 557 | } |
| 558 | |
| 559 | public slots: |
| 560 | void moveToThread(QThread *t) { |
| 561 | QObject::moveToThread(thread: t); |
| 562 | } |
| 563 | }; |
| 564 | |
| 565 | } |
| 566 | |
| 567 | void tst_QEventLoop::deliverInDefinedOrder() |
| 568 | { |
| 569 | using namespace DeliverInDefinedOrder; |
| 570 | qMetaTypeId<QThread*>(); |
| 571 | QThread threads[NbThread]; |
| 572 | Object objects[NbObject]; |
| 573 | for (int t = 0; t < NbThread; t++) { |
| 574 | threads[t].start(); |
| 575 | } |
| 576 | |
| 577 | int event = 0; |
| 578 | |
| 579 | for (int o = 0; o < NbObject; o++) { |
| 580 | objects[o].moveToThread(t: &threads[o % NbThread]); |
| 581 | for (int e = 0; e < NbEvent; e++) { |
| 582 | int q = e % NbEventQueue; |
| 583 | QCoreApplication::postEvent(receiver: &objects[o], event: new CustomEvent(q, ++event) , priority: q); |
| 584 | if (e % 7) |
| 585 | QMetaObject::invokeMethod(obj: &objects[o], member: "moveToThread" , type: Qt::QueuedConnection, Q_ARG(QThread*, &threads[(e+o)%NbThread])); |
| 586 | } |
| 587 | } |
| 588 | |
| 589 | for (int o = 0; o < NbObject; o++) { |
| 590 | QTRY_COMPARE(objects[o].count, int(NbEvent)); |
| 591 | } |
| 592 | |
| 593 | for (int t = 0; t < NbThread; t++) { |
| 594 | threads[t].quit(); |
| 595 | threads[t].wait(); |
| 596 | } |
| 597 | |
| 598 | } |
| 599 | |
| 600 | class JobObject : public QObject |
| 601 | { |
| 602 | Q_OBJECT |
| 603 | public: |
| 604 | |
| 605 | explicit JobObject(QEventLoop *loop, QObject *parent = 0) |
| 606 | : QObject(parent), locker(loop) |
| 607 | { |
| 608 | } |
| 609 | |
| 610 | explicit JobObject(QObject *parent = 0) |
| 611 | : QObject(parent) |
| 612 | { |
| 613 | } |
| 614 | |
| 615 | public slots: |
| 616 | void start(int timeout = 200) |
| 617 | { |
| 618 | QTimer::singleShot(msec: timeout, receiver: this, SLOT(timeout())); |
| 619 | } |
| 620 | |
| 621 | private slots: |
| 622 | void timeout() |
| 623 | { |
| 624 | emit done(); |
| 625 | deleteLater(); |
| 626 | } |
| 627 | |
| 628 | signals: |
| 629 | void done(); |
| 630 | |
| 631 | private: |
| 632 | QEventLoopLocker locker; |
| 633 | }; |
| 634 | |
| 635 | void tst_QEventLoop::testQuitLock() |
| 636 | { |
| 637 | QEventLoop eventLoop; |
| 638 | |
| 639 | QEventLoopPrivate* privateClass = static_cast<QEventLoopPrivate*>(QObjectPrivate::get(o: &eventLoop)); |
| 640 | |
| 641 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 0); |
| 642 | |
| 643 | JobObject *job1 = new JobObject(&eventLoop, this); |
| 644 | job1->start(timeout: 500); |
| 645 | |
| 646 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 1); |
| 647 | |
| 648 | eventLoop.exec(); |
| 649 | |
| 650 | QCOMPARE(privateClass->quitLockRef.loadRelaxed(), 0); |
| 651 | |
| 652 | |
| 653 | job1 = new JobObject(&eventLoop, this); |
| 654 | job1->start(timeout: 200); |
| 655 | |
| 656 | JobObject *previousJob = job1; |
| 657 | for (int i = 0; i < 9; ++i) { |
| 658 | JobObject *subJob = new JobObject(&eventLoop, this); |
| 659 | connect(sender: previousJob, SIGNAL(done()), receiver: subJob, SLOT(start())); |
| 660 | previousJob = subJob; |
| 661 | } |
| 662 | |
| 663 | eventLoop.exec(); |
| 664 | } |
| 665 | |
| 666 | QTEST_MAIN(tst_QEventLoop) |
| 667 | #include "tst_qeventloop.moc" |
| 668 | |