Synchronize QEventDispatcherWin32::processEvents() with QCoreApplication

QCoreApplication has a special internal mechanism to control whether
the event dispatcher should block after delivering the posted events.

To handle queued connections in nested loops properly, we should use
that functionality.

Pick-to: 5.15
Fixes: QTBUG-85981
Change-Id: I124179a23b26a995cf95ed379e97bfa62c95f42a
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
bb10
Alex Trotsenko 2020-08-18 15:07:38 +03:00
parent 5449bddb04
commit fe4b246446
2 changed files with 29 additions and 1 deletions

View File

@ -529,6 +529,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
// QCoreApplication::sendPostedEvents() takes care about recursions.
sendPostedEvents();
auto threadData = d->threadData.loadRelaxed();
bool canWait;
bool retVal = false;
do {
@ -599,7 +600,8 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
// wait for message
canWait = (!retVal
&& !d->interrupt.loadRelaxed()
&& (flags & QEventLoop::WaitForMoreEvents));
&& flags.testFlag(QEventLoop::WaitForMoreEvents)
&& threadData->canWaitLocked());
if (canWait) {
emit aboutToBlock();
MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);

View File

@ -67,6 +67,7 @@ private slots:
void sendPostedEvents_data();
void sendPostedEvents();
void processEventsOnlySendsQueuedEvents();
void postedEventsPingPong();
void eventLoopExit();
};
@ -349,6 +350,31 @@ void tst_QEventDispatcher::processEventsOnlySendsQueuedEvents()
QCOMPARE(object.eventsReceived, 4);
}
void tst_QEventDispatcher::postedEventsPingPong()
{
QEventLoop mainLoop;
// We need to have at least two levels of nested loops
// for the posted event to get stuck (QTBUG-85981).
QMetaObject::invokeMethod(this, [this, &mainLoop]() {
QMetaObject::invokeMethod(this, [&mainLoop]() {
// QEventLoop::quit() should be invoked on the next
// iteration of mainLoop.exec().
QMetaObject::invokeMethod(&mainLoop, &QEventLoop::quit,
Qt::QueuedConnection);
}, Qt::QueuedConnection);
mainLoop.processEvents();
}, Qt::QueuedConnection);
// We should use Qt::CoarseTimer on Windows, to prevent event
// dispatcher from sending a posted event.
QTimer::singleShot(500, Qt::CoarseTimer, [&mainLoop]() {
mainLoop.exit(1);
});
QCOMPARE(mainLoop.exec(), 0);
}
void tst_QEventDispatcher::eventLoopExit()
{
// This test was inspired by QTBUG-79477. A particular