Merge "QEventDispatcher(Win): Always honor interrupted status to avoid races"
This commit is contained in:
commit
d76c0e3224
@ -53,7 +53,7 @@
|
|||||||
{
|
{
|
||||||
QDeadlineTimer deadline(msecs);
|
QDeadlineTimer deadline(msecs);
|
||||||
do {
|
do {
|
||||||
if (readFromDevice(deadline.remainingTime())
|
if (readFromDevice(deadline.remainingTime()))
|
||||||
break;
|
break;
|
||||||
waitForReadyRead(deadline);
|
waitForReadyRead(deadline);
|
||||||
} while (!deadline.hasExpired());
|
} while (!deadline.hasExpired());
|
||||||
|
@ -475,13 +475,17 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
|
|||||||
{
|
{
|
||||||
Q_D(QEventDispatcherWin32);
|
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();
|
emit awake();
|
||||||
|
|
||||||
// To prevent livelocks, send posted events once per iteration.
|
// To prevent livelocks, send posted events once per iteration.
|
||||||
// QCoreApplication::sendPostedEvents() takes care about recursions.
|
// QCoreApplication::sendPostedEvents() takes care about recursions.
|
||||||
sendPostedEvents();
|
sendPostedEvents();
|
||||||
|
|
||||||
|
if (wasInterrupted)
|
||||||
|
return false;
|
||||||
|
|
||||||
auto threadData = d->threadData.loadRelaxed();
|
auto threadData = d->threadData.loadRelaxed();
|
||||||
bool canWait;
|
bool canWait;
|
||||||
bool retVal = false;
|
bool retVal = false;
|
||||||
|
@ -71,6 +71,7 @@ private slots:
|
|||||||
void processEventsOnlySendsQueuedEvents();
|
void processEventsOnlySendsQueuedEvents();
|
||||||
void postedEventsPingPong();
|
void postedEventsPingPong();
|
||||||
void eventLoopExit();
|
void eventLoopExit();
|
||||||
|
void interruptTrampling();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool tst_QEventDispatcher::event(QEvent *e)
|
bool tst_QEventDispatcher::event(QEvent *e)
|
||||||
@ -421,5 +422,31 @@ void tst_QEventDispatcher::eventLoopExit()
|
|||||||
QVERIFY(!timeoutObserved);
|
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)
|
QTEST_MAIN(tst_QEventDispatcher)
|
||||||
#include "tst_qeventdispatcher.moc"
|
#include "tst_qeventdispatcher.moc"
|
||||||
|
@ -74,6 +74,7 @@ void tst_QShortcut::trigger()
|
|||||||
new QShortcut(Qt::CTRL | Qt::Key_Q, &w, SLOT(close()));
|
new QShortcut(Qt::CTRL | Qt::Key_Q, &w, SLOT(close()));
|
||||||
w.show();
|
w.show();
|
||||||
QVERIFY(QTest::qWaitForWindowExposed(&w));
|
QVERIFY(QTest::qWaitForWindowExposed(&w));
|
||||||
|
QTRY_VERIFY(QGuiApplication::applicationState() == Qt::ApplicationActive);
|
||||||
sendKey(&w, Qt::Key_Q, 'q', Qt::ControlModifier);
|
sendKey(&w, Qt::Key_Q, 'q', Qt::ControlModifier);
|
||||||
QTRY_VERIFY(!w.isVisible());
|
QTRY_VERIFY(!w.isVisible());
|
||||||
}
|
}
|
||||||
|
@ -1480,6 +1480,9 @@ void tst_QOpenGL::glxContextWrap()
|
|||||||
if (QGuiApplication::platformName().startsWith(QLatin1String("offscreen"), Qt::CaseInsensitive))
|
if (QGuiApplication::platformName().startsWith(QLatin1String("offscreen"), Qt::CaseInsensitive))
|
||||||
QSKIP("Offscreen: This fails.");
|
QSKIP("Offscreen: This fails.");
|
||||||
|
|
||||||
|
if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive))
|
||||||
|
QSKIP("Fails on Wayland.");
|
||||||
|
|
||||||
QWindow *window = new QWindow;
|
QWindow *window = new QWindow;
|
||||||
window->setSurfaceType(QWindow::OpenGLSurface);
|
window->setSurfaceType(QWindow::OpenGLSurface);
|
||||||
window->setGeometry(0, 0, 10, 10);
|
window->setGeometry(0, 0, 10, 10);
|
||||||
|
@ -552,10 +552,11 @@ void tst_QDialog::keepPositionOnClose()
|
|||||||
dialog.setWindowTitle(QTest::currentTestFunction());
|
dialog.setWindowTitle(QTest::currentTestFunction());
|
||||||
const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
|
const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry();
|
||||||
dialog.resize(availableGeometry.size() / 4);
|
dialog.resize(availableGeometry.size() / 4);
|
||||||
const QPoint pos = availableGeometry.topLeft() + QPoint(100, 100);
|
QPoint pos = availableGeometry.topLeft() + QPoint(100, 100);
|
||||||
dialog.move(pos);
|
dialog.move(pos);
|
||||||
dialog.show();
|
dialog.show();
|
||||||
QVERIFY(QTest::qWaitForWindowExposed(&dialog));
|
QVERIFY(QTest::qWaitForWindowExposed(&dialog));
|
||||||
|
pos = dialog.pos();
|
||||||
dialog.close();
|
dialog.close();
|
||||||
dialog.windowHandle()->destroy(); // Emulate a click on close by destroying the window.
|
dialog.windowHandle()->destroy(); // Emulate a click on close by destroying the window.
|
||||||
QTest::qWait(50);
|
QTest::qWait(50);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user