Move XCB event reading to a separate thread.

Work-around for bug in XCB which causes a xcb_wait_for_reply to block
if xcb_poll_for_events() is called simultaneously from a different
thread. If the XCB version is too old this work-around causes even
more problems, so we kill two birds with one stone by only using the
work-around if the XCB version has the recent
xcb_poll_for_queue_event() function, which we also need to read
events from a separate thread with reasonable efficiency.

Change-Id: I8a899dad6ded381ce42cba0112e77da3c8aa6887
Reviewed-on: http://codereview.qt.nokia.com/3612
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
This commit is contained in:
Samuel Rødal 2011-08-25 13:39:07 +02:00 committed by Lars Knoll
parent 7b686abdd5
commit 794c81c1ea
6 changed files with 212 additions and 20 deletions

View File

@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the config.tests of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <xcb/xcb.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xfixes.h>
#include <xcb/xcb_image.h>
#include <xcb/xcb_keysyms.h>
#include <xcb/sync.h>
#include <xcb/shm.h>
int main(int, char **)
{
int primaryScreen = 0;
xcb_connection_t *connection = xcb_connect("", &primaryScreen);
xcb_generic_event_t *event = xcb_poll_for_queued_event(connection);
return 0;
}

View File

@ -0,0 +1,5 @@
SOURCES = xcb-poll-for-queued-event.cpp
CONFIG -= qt
LIBS += -lxcb -lxcb-image -lxcb-keysyms -lxcb-icccm -lxcb-sync -lxcb-xfixes

12
configure vendored
View File

@ -750,6 +750,7 @@ CFG_DECORATION_PLUGIN=
CFG_XINPUT=runtime
CFG_XKB=auto
CFG_XCB=auto
CFG_XCB_LIMITED=yes
CFG_NIS=auto
CFG_CUPS=auto
CFG_ICONV=auto
@ -6248,6 +6249,11 @@ if [ "$PLATFORM_QPA" = "yes" ]; then
QT_CONFIG="$QT_CONFIG xcb-render"
fi
if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/qpa/xcb-poll-for-queued-event "xcb-poll-for-queued-event" $L_FLAGS $I_FLAGS $l_FLAGS; then
CFG_XCB_LIMITED=no
QT_CONFIG="$QT_CONFIG xcb-poll-for-queued-event"
fi
if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/qpa/xcb-xlib "xcb-xlib" $L_FLAGS $I_FLAGS $l_FLAGS; then
QT_CONFIG="$QT_CONFIG xcb-xlib"
fi
@ -8760,7 +8766,11 @@ if [ "$PLATFORM_MAC" = "yes" ]; then
fi
echo "ICD support ............ $CFG_ICD"
echo "libICU support ......... $CFG_ICU"
echo "Xcb support ............ $CFG_XCB"
if [ "$CFG_XCB_LIMITED" = "yes" ] && [ "$CFG_XCB" = "yes" ]; then
echo "Xcb support ............ limited (old version)"
else
echo "Xcb support ............ $CFG_XCB"
fi
echo
[ "$CFG_PTMALLOC" != "no" ] && echo "Use ptmalloc ........... $CFG_PTMALLOC"

View File

@ -53,6 +53,7 @@
#include <QtAlgorithms>
#include <QSocketNotifier>
#include <QAbstractEventDispatcher>
#include <QTimer>
#include <stdio.h>
#include <errno.h>
@ -114,12 +115,18 @@ QXcbConnection::QXcbConnection(const char *displayName)
if (m_connection)
printf("Successfully connected to display %s\n", m_displayName.constData());
m_reader = new QXcbEventReader(m_connection);
#ifdef XCB_POLL_FOR_QUEUED_EVENT
connect(m_reader, SIGNAL(eventPending()), this, SLOT(processXcbEvents()), Qt::QueuedConnection);
m_reader->start();
#else
QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(xcb_connection()), QSocketNotifier::Read, this);
connect(notifier, SIGNAL(activated(int)), this, SLOT(processXcbEvents()));
QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher;
connect(dispatcher, SIGNAL(aboutToBlock()), this, SLOT(processXcbEvents()));
connect(dispatcher, SIGNAL(awake()), this, SLOT(processXcbEvents()));
#endif
xcb_prefetch_extension_data (m_connection, &xcb_xfixes_id);
@ -137,6 +144,12 @@ QXcbConnection::QXcbConnection(const char *displayName)
xcb_screen_next(&it);
}
m_connectionEventListener = xcb_generate_id(m_connection);
xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
m_connectionEventListener, m_screens.at(0)->root(),
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
m_screens.at(0)->screen()->root_visual, 0, 0);
initializeXFixes();
initializeXRender();
@ -157,6 +170,12 @@ QXcbConnection::~QXcbConnection()
qDeleteAll(m_screens);
#ifdef XCB_POLL_FOR_QUEUED_EVENT
sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION);
m_reader->wait();
#endif
delete m_reader;
#ifdef XCB_USE_XLIB
XCloseDisplay((Display *)m_xlib_display);
#else
@ -531,16 +550,72 @@ void QXcbConnection::addPeekFunc(PeekFunc f)
m_peekFuncs.append(f);
}
#ifdef XCB_POLL_FOR_QUEUED_EVENT
void QXcbEventReader::run()
{
xcb_generic_event_t *event;
while (m_connection && (event = xcb_wait_for_event(m_connection))) {
m_mutex.lock();
addEvent(event);
while (m_connection && (event = xcb_poll_for_queued_event(m_connection)))
addEvent(event);
m_mutex.unlock();
emit eventPending();
}
for (int i = 0; i < m_events.size(); ++i)
free(m_events.at(i));
}
#endif
void QXcbEventReader::addEvent(xcb_generic_event_t *event)
{
if ((event->response_type & ~0x80) == XCB_CLIENT_MESSAGE
&& ((xcb_client_message_event_t *)event)->type == QXcbAtom::_QT_CLOSE_CONNECTION)
m_connection = 0;
m_events << event;
}
QList<xcb_generic_event_t *> *QXcbEventReader::lock()
{
m_mutex.lock();
#ifndef XCB_POLL_FOR_QUEUED_EVENT
while (xcb_generic_event_t *event = xcb_poll_for_event(m_connection))
m_events << event;
#endif
return &m_events;
}
void QXcbEventReader::unlock()
{
m_mutex.unlock();
}
void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom atom, uint id)
{
xcb_client_message_event_t event;
memset(&event, 0, sizeof(event));
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
event.sequence = 0;
event.window = m_connectionEventListener;
event.type = atom;
event.data.data32[0] = id;
Q_XCB_CALL(xcb_send_event(xcb_connection(), false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event));
xcb_flush(xcb_connection());
}
void QXcbConnection::processXcbEvents()
{
while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection()))
eventqueue.append(event);
QList<xcb_generic_event_t *> *eventqueue = m_reader->lock();
for(int i = 0; i < eventqueue.size(); ++i) {
xcb_generic_event_t *event = eventqueue.at(i);
for(int i = 0; i < eventqueue->size(); ++i) {
xcb_generic_event_t *event = eventqueue->at(i);
if (!event)
continue;
eventqueue[i] = 0;
(*eventqueue)[i] = 0;
uint response_type = event->response_type & ~0x80;
@ -562,7 +637,9 @@ void QXcbConnection::processXcbEvents()
free(event);
}
eventqueue.clear();
eventqueue->clear();
m_reader->unlock();
// Indicate with a null event that the event the callbacks are waiting for
// is not in the queue currently.
@ -591,19 +668,21 @@ void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t *
window->handleClientMessageEvent(event);
}
xcb_generic_event_t *QXcbConnection::checkEvent(int type)
{
while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection()))
eventqueue.append(event);
QList<xcb_generic_event_t *> *eventqueue = m_reader->lock();
for (int i = 0; i < eventqueue.size(); ++i) {
xcb_generic_event_t *event = eventqueue.at(i);
for (int i = 0; i < eventqueue->size(); ++i) {
xcb_generic_event_t *event = eventqueue->at(i);
if (event->response_type == type) {
eventqueue[i] = 0;
(*eventqueue)[i] = 0;
m_reader->unlock();
return event;
}
}
m_reader->unlock();
return 0;
}
@ -646,6 +725,8 @@ static const char * xcb_atomnames = {
"_QT_SCROLL_DONE\0"
"_QT_INPUT_ENCODING\0"
"_QT_CLOSE_CONNECTION\0"
"_MOTIF_WM_HINTS\0"
"DTWM_IS_RUNNING\0"

View File

@ -46,7 +46,9 @@
#include <QHash>
#include <QList>
#include <QMutex>
#include <QObject>
#include <QThread>
#include <QVector>
#define Q_XCB_DEBUG
@ -100,6 +102,9 @@ namespace QXcbAtom {
_QT_SCROLL_DONE,
_QT_INPUT_ENCODING,
// Qt/XCB specific
_QT_CLOSE_CONNECTION,
_MOTIF_WM_HINTS,
DTWM_IS_RUNNING,
@ -222,6 +227,33 @@ namespace QXcbAtom {
};
}
class QXcbEventReader : public QThread
{
Q_OBJECT
public:
QXcbEventReader(xcb_connection_t *connection)
: m_connection(connection)
{
}
#ifdef XCB_POLL_FOR_QUEUED_EVENT
void run();
#endif
QList<xcb_generic_event_t *> *lock();
void unlock();
signals:
void eventPending();
private:
void addEvent(xcb_generic_event_t *event);
QMutex m_mutex;
QList<xcb_generic_event_t *> m_events;
xcb_connection_t *m_connection;
};
class QAbstractEventDispatcher;
class QXcbConnection : public QObject
{
@ -314,6 +346,8 @@ private:
QByteArray m_displayName;
xcb_window_t m_connectionEventListener;
QXcbKeyboard *m_keyboard;
QXcbClipboard *m_clipboard;
QXcbDrag *m_drag;
@ -322,7 +356,7 @@ private:
#if defined(XCB_USE_XLIB)
void *m_xlib_display;
#endif
QXcbEventReader *m_reader;
#ifdef XCB_USE_DRI2
uint32_t m_dri2_major;
uint32_t m_dri2_minor;
@ -345,7 +379,6 @@ private:
template <typename cookie_t>
friend cookie_t q_xcb_call_template(const cookie_t &cookie, QXcbConnection *connection, const char *file, int line);
#endif
QVector<xcb_generic_event_t *> eventqueue;
WindowMapper m_mapper;
@ -359,16 +392,17 @@ private:
template<typename T>
xcb_generic_event_t *QXcbConnection::checkEvent(const T &checker)
{
while (xcb_generic_event_t *event = xcb_poll_for_event(xcb_connection()))
eventqueue.append(event);
QList<xcb_generic_event_t *> *eventqueue = m_reader->lock();
for (int i = 0; i < eventqueue.size(); ++i) {
xcb_generic_event_t *event = eventqueue.at(i);
for (int i = 0; i < eventqueue->size(); ++i) {
xcb_generic_event_t *event = eventqueue->at(i);
if (checker.check(event)) {
eventqueue[i] = 0;
(*eventqueue)[i] = 0;
m_reader->unlock();
return event;
}
}
m_reader->unlock();
return 0;
}

View File

@ -37,6 +37,9 @@ HEADERS = \
qxcbcursor.h \
qxcbimage.h
contains(QT_CONFIG, xcb-poll-for-queued-event) {
DEFINES += XCB_POLL_FOR_QUEUED_EVENT
}
# needed by GLX, Xcursor, XLookupString, ...
contains(QT_CONFIG, xcb-xlib) {