summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp138
-rw-r--r--src/corelib/kernel/qeventdispatcher_win_p.h4
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp2
-rw-r--r--tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp83
4 files changed, 149 insertions, 78 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index 743df3844c..7e6d478473 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -99,14 +99,12 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0),
getMessageHook(0), sendPostedEventsTimerId(0), wakeUps(0),
- activateNotifiersPosted(false), winEventNotifierActivatedEvent(NULL)
+ activateNotifiersPosted(false), activateEventNotifiersPosted(false)
{
}
QEventDispatcherWin32Private::~QEventDispatcherWin32Private()
{
- if (winEventNotifierActivatedEvent)
- CloseHandle(winEventNotifierActivatedEvent);
if (internalHwnd)
DestroyWindow(internalHwnd);
}
@@ -457,6 +455,14 @@ void QEventDispatcherWin32Private::postActivateSocketNotifiers()
activateNotifiersPosted = PostMessage(internalHwnd, WM_QT_ACTIVATENOTIFIERS, 0, 0);
}
+void QEventDispatcherWin32Private::postActivateEventNotifiers()
+{
+ Q_Q(QEventDispatcherWin32);
+
+ if (!activateEventNotifiersPosted.fetchAndStoreRelease(true))
+ QCoreApplication::postEvent(q, new QEvent(QEvent::WinEventAct));
+}
+
void QEventDispatcherWin32::createInternalHwnd()
{
Q_D(QEventDispatcherWin32);
@@ -526,102 +532,78 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
bool canWait;
bool retVal = false;
do {
- DWORD waitRet = 0;
- DWORD nCount = 0;
- HANDLE *pHandles = nullptr;
- if (d->winEventNotifierActivatedEvent) {
- nCount = 1;
- pHandles = &d->winEventNotifierActivatedEvent;
- }
QVarLengthArray<MSG> processedTimers;
while (!d->interrupt.loadRelaxed()) {
MSG msg;
- bool haveMessage;
if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
// process queued user input events
- haveMessage = true;
msg = d->queuedUserInputEvents.takeFirst();
} else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
// process queued socket events
- haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
- } else {
- haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
- if (haveMessage) {
- if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)
- && isUserInputMessage(msg.message)) {
- // queue user input events for later processing
- d->queuedUserInputEvents.append(msg);
- continue;
- }
- if ((flags & QEventLoop::ExcludeSocketNotifiers)
- && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
- // queue socket events for later processing
- d->queuedSocketEvents.append(msg);
- continue;
- }
+ } else if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+ if (flags.testFlag(QEventLoop::ExcludeUserInputEvents)
+ && isUserInputMessage(msg.message)) {
+ // queue user input events for later processing
+ d->queuedUserInputEvents.append(msg);
+ continue;
}
- }
- if (!haveMessage) {
- // no message - check for signalled objects
- waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
- if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
- // a new message has arrived, process it
+ if ((flags & QEventLoop::ExcludeSocketNotifiers)
+ && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
+ // queue socket events for later processing
+ d->queuedSocketEvents.append(msg);
continue;
}
+ } else if (MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE)
+ == WAIT_OBJECT_0) {
+ // a new message has arrived, process it
+ continue;
+ } else {
+ // nothing to do, so break
+ break;
+ }
+
+ if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
+ // Set result to 'true' because the message was sent by wakeUp().
+ retVal = true;
+ continue;
}
- if (haveMessage) {
- if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
- // Set result to 'true' because the message was sent by wakeUp().
- retVal = true;
+ if (msg.message == WM_TIMER) {
+ // Skip timer event intended for use inside foreign loop.
+ if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
continue;
- }
- if (msg.message == WM_TIMER) {
- // Skip timer event intended for use inside foreign loop.
- if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
- continue;
-
- // avoid live-lock by keeping track of the timers we've already sent
- bool found = false;
- for (int i = 0; !found && i < processedTimers.count(); ++i) {
- const MSG processed = processedTimers.constData()[i];
- found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
- }
- if (found)
- continue;
- processedTimers.append(msg);
- } else if (msg.message == WM_QUIT) {
- if (QCoreApplication::instance())
- QCoreApplication::instance()->quit();
- return false;
- }
- if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
+ // avoid live-lock by keeping track of the timers we've already sent
+ bool found = false;
+ for (int i = 0; !found && i < processedTimers.count(); ++i) {
+ const MSG processed = processedTimers.constData()[i];
+ found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
}
- } else if (waitRet - WAIT_OBJECT_0 < nCount) {
- activateEventNotifiers();
- } else {
- // nothing todo so break
- break;
+ if (found)
+ continue;
+ processedTimers.append(msg);
+ } else if (msg.message == WM_QUIT) {
+ if (QCoreApplication::instance())
+ QCoreApplication::instance()->quit();
+ return false;
+ }
+
+ if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
}
retVal = true;
}
- // still nothing - wait for message or signalled objects
+ // wait for message
canWait = (!retVal
&& !d->interrupt.loadRelaxed()
&& (flags & QEventLoop::WaitForMoreEvents));
if (canWait) {
emit aboutToBlock();
- waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+ MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
emit awake();
- if (waitRet - WAIT_OBJECT_0 < nCount) {
- activateEventNotifiers();
- retVal = true;
- }
}
} while (canWait);
@@ -871,12 +853,11 @@ bool QEventDispatcherWin32::registerEventNotifier(QWinEventNotifier *notifier)
if (d->winEventNotifierList.contains(notifier))
return true;
+ createInternalHwnd();
+
d->winEventNotifierList.append(notifier);
d->winEventNotifierListModified = true;
- if (!d->winEventNotifierActivatedEvent)
- d->winEventNotifierActivatedEvent = CreateEvent(0, TRUE, FALSE, nullptr);
-
return QWinEventNotifierPrivate::get(notifier)->registerWaitObject();
}
@@ -909,7 +890,9 @@ void QEventDispatcherWin32::doUnregisterEventNotifier(QWinEventNotifier *notifie
void QEventDispatcherWin32::activateEventNotifiers()
{
Q_D(QEventDispatcherWin32);
- ResetEvent(d->winEventNotifierActivatedEvent);
+
+ // Enable WM_QT_ACTIVATEWINEVENTS posting.
+ d->activateEventNotifiersPosted.fetchAndStoreAcquire(false);
// Activate signaled notifiers. Our winEventNotifierList can be modified in activation slots.
do {
@@ -1055,6 +1038,9 @@ bool QEventDispatcherWin32::event(QEvent *e)
case QEvent::Timer:
d->sendTimerEvent(static_cast<const QTimerEvent*>(e)->timerId());
break;
+ case QEvent::WinEventAct:
+ activateEventNotifiers();
+ break;
default:
break;
}
diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h
index dbb30ab568..078329c43c 100644
--- a/src/corelib/kernel/qeventdispatcher_win_p.h
+++ b/src/corelib/kernel/qeventdispatcher_win_p.h
@@ -54,6 +54,7 @@
#include "QtCore/qabstracteventdispatcher.h"
#include "QtCore/qt_windows.h"
#include "QtCore/qhash.h"
+#include "QtCore/qatomic.h"
#include "qabstracteventdispatcher_p.h"
@@ -193,8 +194,9 @@ public:
bool closingDown = false;
bool winEventNotifierListModified = false;
- HANDLE winEventNotifierActivatedEvent;
+ QAtomicInt activateEventNotifiersPosted;
QList<QWinEventNotifier *> winEventNotifierList;
+ void postActivateEventNotifiers();
void activateEventNotifier(QWinEventNotifier * wen);
QList<MSG> queuedUserInputEvents;
diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp
index b306e3aba1..8cf6b018d4 100644
--- a/src/corelib/kernel/qwineventnotifier.cpp
+++ b/src/corelib/kernel/qwineventnotifier.cpp
@@ -246,7 +246,7 @@ static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/)
QEventDispatcherWin32Private *edp = QEventDispatcherWin32Private::get(
static_cast<QEventDispatcherWin32 *>(eventDispatcher));
++nd->signaledCount;
- SetEvent(edp->winEventNotifierActivatedEvent);
+ edp->postActivateEventNotifiers();
}
bool QWinEventNotifierPrivate::registerWaitObject()
diff --git a/tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp b/tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp
index 5fd7079a6a..6f2ed47b72 100644
--- a/tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp
+++ b/tests/auto/gui/kernel/noqteventloop/tst_noqteventloop.cpp
@@ -37,6 +37,7 @@
#include <QtNetwork/qtcpsocket.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qtimer.h>
+#include <QtCore/qwineventnotifier.h>
#include <QtCore/qt_windows.h>
@@ -50,6 +51,8 @@ class tst_NoQtEventLoop : public QObject
private slots:
void consumeMouseEvents();
void consumeSocketEvents();
+ void consumeWinEvents_data();
+ void consumeWinEvents();
void deliverEventsInLivelock();
void postingWithNoYieldFlag();
};
@@ -314,6 +317,86 @@ void tst_NoQtEventLoop::consumeSocketEvents()
QVERIFY(server.hasPendingConnections());
}
+void tst_NoQtEventLoop::consumeWinEvents_data()
+{
+ QTest::addColumn<bool>("peeking");
+ QTest::addColumn<bool>("nestedLoops");
+ QTest::addColumn<bool>("nativeMainLoop");
+
+ QTest::newRow("PeekMessage, single loop") << true << false << true;
+ QTest::newRow("GetMessage, single loop") << false << false << true;
+
+ QTest::newRow("PeekMessage, nested loops") << true << true << true;
+ QTest::newRow("GetMessage, nested loops") << false << true << true;
+ QTest::newRow("PeekMessage, nested loops, Qt main loop") << true << true << false;
+ QTest::newRow("GetMessage, nested loops, Qt main loop") << false << true << false;
+}
+
+void tst_NoQtEventLoop::consumeWinEvents()
+{
+ QFETCH(bool, peeking);
+ QFETCH(bool, nestedLoops);
+ QFETCH(bool, nativeMainLoop);
+ int argc = 1;
+ char *argv[] = { const_cast<char *>("test"), 0 };
+ QGuiApplication app(argc, argv);
+
+ HANDLE winEvent = ::CreateEvent(0, FALSE, FALSE, 0);
+ QVERIFY(winEvent != NULL);
+ bool notifierActivated = false;
+ QWinEventNotifier notifier(winEvent);
+ connect(&notifier, &QWinEventNotifier::activated, [&notifierActivated]() {
+ notifierActivated = true;
+ });
+
+ bool timeExpired = false;
+ QTimer expiredTimer;
+ connect(&expiredTimer, &QTimer::timeout, [&timeExpired]() {
+ timeExpired = true;
+ });
+ expiredTimer.start(3000);
+
+ auto runNativeEventLoop = [&timeExpired, &notifierActivated, peeking]() -> void {
+ MSG msg;
+ while (!(timeExpired || notifierActivated)) {
+ if (peeking) {
+ if (!::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ QThread::msleep(100);
+ continue;
+ }
+ } else {
+ if (::GetMessage(&msg, NULL, 0, 0) <= 0)
+ break;
+ }
+
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+ };
+
+ QTimer::singleShot(0, [winEvent, nestedLoops, runNativeEventLoop]() {
+ ::SetEvent(winEvent);
+ if (nestedLoops)
+ runNativeEventLoop();
+ });
+
+ // Exec main message loop
+ if (nativeMainLoop) {
+ runNativeEventLoop();
+ } else {
+ QEventLoop loop;
+ connect(&notifier, &QWinEventNotifier::activated,
+ &loop, &QEventLoop::quit);
+ connect(&expiredTimer, &QTimer::timeout,
+ &loop, &QEventLoop::quit);
+
+ loop.exec();
+ }
+
+ QVERIFY(notifierActivated);
+ QVERIFY(!timeExpired);
+}
+
void tst_NoQtEventLoop::deliverEventsInLivelock()
{
int argc = 1;