Merge "QEventDispatcher(Win): Always honor interrupted status to avoid races"

This commit is contained in:
Mårten Nordheim 2021-03-05 14:33:01 +01:00 committed by Qt CI Bot
commit d76c0e3224
6 changed files with 39 additions and 3 deletions

View File

@ -53,7 +53,7 @@
{
QDeadlineTimer deadline(msecs);
do {
if (readFromDevice(deadline.remainingTime())
if (readFromDevice(deadline.remainingTime()))
break;
waitForReadyRead(deadline);
} while (!deadline.hasExpired());

View File

@ -475,13 +475,17 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
d->interrupt.storeRelaxed(false);
// We don't know _when_ the interrupt occurred so we have to honor it.
const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false);
emit awake();
// To prevent livelocks, send posted events once per iteration.
// QCoreApplication::sendPostedEvents() takes care about recursions.
sendPostedEvents();
if (wasInterrupted)
return false;
auto threadData = d->threadData.loadRelaxed();
bool canWait;
bool retVal = false;

View File

@ -71,6 +71,7 @@ private slots:
void processEventsOnlySendsQueuedEvents();
void postedEventsPingPong();
void eventLoopExit();
void interruptTrampling();
};
bool tst_QEventDispatcher::event(QEvent *e)
@ -421,5 +422,31 @@ void tst_QEventDispatcher::eventLoopExit()
QVERIFY(!timeoutObserved);
}
// Based on QTBUG-91539: In the event dispatcher on Windows we overwrite the
// interrupt once we start processing events (this pattern is also in the 'unix' dispatcher)
// which would lead the dispatcher to accidentally ignore certain interrupts and,
// as in the bug report, would not quit, leaving the thread alive and running.
void tst_QEventDispatcher::interruptTrampling()
{
class WorkerThread : public QThread
{
void run() override {
auto dispatcher = eventDispatcher();
QVERIFY(dispatcher);
dispatcher->processEvents(QEventLoop::AllEvents);
QTimer::singleShot(0, [dispatcher]() {
dispatcher->wakeUp();
});
dispatcher->processEvents(QEventLoop::WaitForMoreEvents);
dispatcher->interrupt();
dispatcher->processEvents(QEventLoop::WaitForMoreEvents);
}
};
WorkerThread thread;
thread.start();
QVERIFY(thread.wait(1000));
QVERIFY(thread.isFinished());
}
QTEST_MAIN(tst_QEventDispatcher)
#include "tst_qeventdispatcher.moc"

View File

@ -74,6 +74,7 @@ void tst_QShortcut::trigger()
new QShortcut(Qt::CTRL | Qt::Key_Q, &w, SLOT(close()));
w.show();
QVERIFY(QTest::qWaitForWindowExposed(&w));
QTRY_VERIFY(QGuiApplication::applicationState() == Qt::ApplicationActive);
sendKey(&w, Qt::Key_Q, 'q', Qt::ControlModifier);
QTRY_VERIFY(!w.isVisible());
}

View File

@ -1480,6 +1480,9 @@ void tst_QOpenGL::glxContextWrap()
if (QGuiApplication::platformName().startsWith(QLatin1String("offscreen"), Qt::CaseInsensitive))
QSKIP("Offscreen: This fails.");
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
QSKIP("Fails on Wayland.");
QWindow *window = new QWindow;
window->setSurfaceType(QWindow::OpenGLSurface);
window->setGeometry(0, 0, 10, 10);

View File

@ -552,10 +552,11 @@ void tst_QDialog::keepPositionOnClose()
dialog.setWindowTitle(QTest::currentTestFunction());
const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
dialog.resize(availableGeometry.size() / 4);
const QPoint pos = availableGeometry.topLeft() + QPoint(100, 100);
QPoint pos = availableGeometry.topLeft() + QPoint(100, 100);
dialog.move(pos);
dialog.show();
QVERIFY(QTest::qWaitForWindowExposed(&dialog));
pos = dialog.pos();
dialog.close();
dialog.windowHandle()->destroy(); // Emulate a click on close by destroying the window.
QTest::qWait(50);