Prevent busy loop in glib event dispatcher

.. when running event loop with QEventLoop::ExcludeUserInputEvents.

In a properly functioning code, g_main_context_iteration is expected
to block until any event source becomes ready to dispatch an event
(or interrupt occurs). Qt provides several custom event sources to
the Glib event loop. The bug (busy loop) was caused by faulty event
source implementation when QEventLoop::ExcludeUserInputEvents is set.
As long as the window system's event queue was not empty, we signaled
to the event dispatcher that there is an event ready to be dispatched.
This results in the dispatcher calling the relevant dispatch function
(which does handle the ExcludeUserInputEvents flag correctly). As we
do not dispatch user events, the window system's event queue never
becomes empty and we enter a busy loop (CPU running at 100%) where we
signal that we have events to dispatch, but we actually do not dispatch
them and g_main_context_iteration never gets to block.

This busy loop can cause blocking GTK functions such as gtk_dialog_run()
never return.

Task-number: QTBUG-59760
Task-number: QTBUG-57101
Change-Id: I545b7951108eeaba019614ae8f5a1168c8b26c27
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Gunnar Sletta <gunnar@crimson.no>
bb10
Gatis Paeglis 2017-04-05 14:00:00 +02:00
parent c2cac34e94
commit fbb485d4f6
4 changed files with 27 additions and 6 deletions

View File

@ -141,6 +141,11 @@ int QWindowSystemInterfacePrivate::windowSystemEventsQueued()
return windowSystemEventQueue.count();
}
bool QWindowSystemInterfacePrivate::nonUserInputEventsQueued()
{
return windowSystemEventQueue.nonUserInputEventsQueued();
}
QWindowSystemInterfacePrivate::WindowSystemEvent * QWindowSystemInterfacePrivate::getWindowSystemEvent()
{
return windowSystemEventQueue.takeFirstOrReturnNull();
@ -955,6 +960,11 @@ int QWindowSystemInterface::windowSystemEventsQueued()
return QWindowSystemInterfacePrivate::windowSystemEventsQueued();
}
bool QWindowSystemInterface::nonUserInputEventsQueued()
{
return QWindowSystemInterfacePrivate::nonUserInputEventsQueued();
}
// --------------------- QtTestLib support ---------------------
// The following functions are used by testlib, and need to be synchronous to avoid

View File

@ -246,6 +246,7 @@ public:
static bool flushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
static void deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags);
static int windowSystemEventsQueued();
static bool nonUserInputEventsQueued();
};
#ifndef QT_NO_DEBUG_STREAM

View File

@ -458,6 +458,14 @@ public:
return impl.takeAt(i);
return 0;
}
bool nonUserInputEventsQueued()
{
const QMutexLocker locker(&mutex);
for (int i = 0; i < impl.size(); ++i)
if (!(impl.at(i)->type & QWindowSystemInterfacePrivate::UserInputEvent))
return true;
return false;
}
void append(WindowSystemEvent *e)
{ const QMutexLocker locker(&mutex); impl.append(e); }
int count() const
@ -488,6 +496,7 @@ public:
static WindowSystemEventList windowSystemEventQueue;
static int windowSystemEventsQueued();
static bool nonUserInputEventsQueued();
static WindowSystemEvent *getWindowSystemEvent();
static WindowSystemEvent *getNonUserInputWindowSystemEvent();
static WindowSystemEvent *peekWindowSystemEvent(EventType t);

View File

@ -46,8 +46,6 @@
#include <glib.h>
#include "private/qguiapplication_p.h"
#include <qdebug.h>
QT_BEGIN_NAMESPACE
struct GUserEventSource
@ -56,12 +54,15 @@ struct GUserEventSource
QPAEventDispatcherGlib *q;
};
static gboolean userEventSourcePrepare(GSource *s, gint *timeout)
static gboolean userEventSourcePrepare(GSource *source, gint *timeout)
{
Q_UNUSED(s)
Q_UNUSED(timeout)
return QWindowSystemInterface::windowSystemEventsQueued() > 0;
GUserEventSource *userEventSource = reinterpret_cast<GUserEventSource *>(source);
QPAEventDispatcherGlib *dispatcher = userEventSource->q;
if (dispatcher->m_flags & QEventLoop::ExcludeUserInputEvents)
return QWindowSystemInterface::nonUserInputEventsQueued();
else
return QWindowSystemInterface::windowSystemEventsQueued() > 0;
}
static gboolean userEventSourceCheck(GSource *source)