direct2d: Use simple event posting to avoid event queue lock up

In rare cases, the Windows event loop can be spinning inside the inner
loop and the message hook is never called. This can be triggered on the
Direct2D platform by opening 32+ window handles.

The issue can be worked around by using the same approach Windows CE uses:
don't rely on the message hook to inform the event loop that the post
message has been delivered. Instead, uninstall the hook and let it be
called directly by the event loop.

Task-number: QTBUG-42428
Change-Id: I10280126dd50729bc260aa5f7029549e2e061c01
Reviewed-by: Louai Al-Khanji <louai.al-khanji@theqtcompany.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com>
This commit is contained in:
Andrew Knight 2014-11-07 11:04:49 +02:00
parent 21e5d77e85
commit 3d70925ee5
4 changed files with 56 additions and 20 deletions

View File

@ -434,9 +434,10 @@ static inline UINT inputTimerMask()
LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
{
QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance());
Q_ASSERT(q != 0);
if (wp == PM_REMOVE) {
QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance());
Q_ASSERT(q != 0);
if (q) {
MSG *msg = (MSG *) lp;
QEventDispatcherWin32Private *d = q->d_func();
@ -472,7 +473,7 @@ LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
#ifdef Q_OS_WINCE
return 0;
#else
return CallNextHookEx(0, code, wp, lp);
return q->d_func()->getMessageHook ? CallNextHookEx(0, code, wp, lp) : 0;
#endif
}
@ -643,15 +644,7 @@ void QEventDispatcherWin32::createInternalHwnd()
return;
d->internalHwnd = qt_create_internal_window(this);
#ifndef Q_OS_WINCE
// setup GetMessage hook needed to drive our posted events
d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());
if (!d->getMessageHook) {
int errorCode = GetLastError();
qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %s",
errorCode, qPrintable(qt_error_string(errorCode)));
}
#endif
installMessageHook();
// register all socket notifiers
QList<int> sockets = (d->sn_read.keys().toSet()
@ -665,6 +658,35 @@ void QEventDispatcherWin32::createInternalHwnd()
d->registerTimer(d->timerVec.at(i));
}
void QEventDispatcherWin32::installMessageHook()
{
Q_D(QEventDispatcherWin32);
if (d->getMessageHook)
return;
#ifndef Q_OS_WINCE
// setup GetMessage hook needed to drive our posted events
d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());
if (!d->getMessageHook) {
int errorCode = GetLastError();
qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %s",
errorCode, qPrintable(qt_error_string(errorCode)));
}
#endif
}
void QEventDispatcherWin32::uninstallMessageHook()
{
Q_D(QEventDispatcherWin32);
#ifndef Q_OS_WINCE
if (d->getMessageHook)
UnhookWindowsHookEx(d->getMessageHook);
#endif
d->getMessageHook = 0;
}
QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent)
: QAbstractEventDispatcher(*new QEventDispatcherWin32Private, parent)
{
@ -750,10 +772,9 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
}
}
if (haveMessage) {
#ifdef Q_OS_WINCE
// WinCE doesn't support hooks at all, so we have to call this by hand :(
(void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);
#endif
if (!d->getMessageHook)
(void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
if (seenWM_QT_SENDPOSTEDEVENTS) {
@ -1134,11 +1155,7 @@ void QEventDispatcherWin32::closingDown()
d->timerVec.clear();
d->timerDict.clear();
#ifndef Q_OS_WINCE
if (d->getMessageHook)
UnhookWindowsHookEx(d->getMessageHook);
d->getMessageHook = 0;
#endif
uninstallMessageHook();
}
bool QEventDispatcherWin32::event(QEvent *e)

View File

@ -67,6 +67,8 @@ class Q_CORE_EXPORT QEventDispatcherWin32 : public QAbstractEventDispatcher
protected:
void createInternalHwnd();
void installMessageHook();
void uninstallMessageHook();
public:
explicit QEventDispatcherWin32(QObject *parent = 0);

View File

@ -39,6 +39,7 @@
#include "qwindowsdirect2dwindow.h"
#include "qwindowscontext.h"
#include "qwindowsguieventdispatcher.h"
#include <qplatformdefs.h>
#include <QtCore/QCoreApplication>
@ -47,6 +48,16 @@
QT_BEGIN_NAMESPACE
class QWindowsDirect2DEventDispatcher : public QWindowsGuiEventDispatcher
{
public:
QWindowsDirect2DEventDispatcher(QObject *parent = 0)
: QWindowsGuiEventDispatcher(parent)
{
uninstallMessageHook(); // ### Workaround for QTBUG-42428
}
};
class QWindowsDirect2DIntegrationPrivate
{
public:
@ -237,6 +248,11 @@ QPlatformBackingStore *QWindowsDirect2DIntegration::createPlatformBackingStore(Q
return new QWindowsDirect2DBackingStore(window);
}
QAbstractEventDispatcher *QWindowsDirect2DIntegration::createEventDispatcher() const
{
return new QWindowsDirect2DEventDispatcher;
}
QWindowsDirect2DContext *QWindowsDirect2DIntegration::direct2DContext() const
{
return &d->m_d2dContext;

View File

@ -56,6 +56,7 @@ public:
QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE;
QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const Q_DECL_OVERRIDE;
QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE;
QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE;
QWindowsDirect2DContext *direct2DContext() const;