From 243c3044b647357ca6df79ac1497ae43de957d31 Mon Sep 17 00:00:00 2001 From: Gatis Paeglis Date: Fri, 31 Aug 2018 13:03:47 +0200 Subject: xcb: lock-free event processing For details how this works refer to the documentation in the patch. The follow-up patches will switch to calling processXcbEvents() on every event loop iteration. With the existing code that would mean frequent locking of shared data (event queue). Acquiring a lock is fast, but lock contention isn't. To avoid potential problems, reimplement xcb event processing to be lock-free. Besides theoretical performance benefits, this definitally improves code readability in qxcbconnection.cpp. Thanks to Mikhail Svetkin for questioning the design of the existing code. Done-with: Mikhail Svetkin Change-Id: I935f2b6ca802580f5c80205aef7b2f9afc172d26 Reviewed-by: Mikhail Svetkin Reviewed-by: Thiago Macieira --- src/plugins/platforms/xcb/qxcbeventqueue.h | 160 +++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/plugins/platforms/xcb/qxcbeventqueue.h (limited to 'src/plugins/platforms/xcb/qxcbeventqueue.h') diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.h b/src/plugins/platforms/xcb/qxcbeventqueue.h new file mode 100644 index 0000000000..5b0d8bf3d6 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbeventqueue.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QXCBEVENTQUEUE_H +#define QXCBEVENTQUEUE_H + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +struct QXcbEventNode { + QXcbEventNode(xcb_generic_event_t *e = nullptr) + : event(e) { } + + xcb_generic_event_t *event; + QXcbEventNode *next = nullptr; + bool fromHeap = false; +}; + +class QXcbConnection; +class QAbstractEventDispatcher; + +class QXcbEventQueue : public QThread +{ + Q_OBJECT +public: + QXcbEventQueue(QXcbConnection *connection); + ~QXcbEventQueue(); + + enum { PoolSize = 100 }; // 2.4 kB with 100 nodes + + enum PeekOption { + PeekDefault = 0, // see qx11info_x11.h for docs + PeekFromCachedIndex = 1, + PeekRetainMatch = 2, + PeekRemoveMatch = 3, + PeekRemoveMatchContinue = 4 + }; + Q_DECLARE_FLAGS(PeekOptions, PeekOption) + + void run() override; + + void registerEventDispatcher(QAbstractEventDispatcher *dispatcher); + + bool isEmpty() const { return m_head == m_flushedTail && !m_head->event; } + xcb_generic_event_t *takeFirst(); + void flushBufferedEvents(); + + // ### peek() and peekEventQueue() could be unified. Note that peekEventQueue() + // is public API exposed via QX11Extras/QX11Info. + template + xcb_generic_event_t *peek(Peeker &&peeker) { + return peek(PeekRemoveMatch, std::forward(peeker)); + } + template + inline xcb_generic_event_t *peek(PeekOption config, Peeker &&peeker); + + qint32 generatePeekerId(); + bool removePeekerId(qint32 peekerId); + + using PeekerCallback = bool (*)(xcb_generic_event_t *event, void *peekerData); + bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr, + PeekOptions option = PeekDefault, qint32 peekerId = -1); +signals: + void eventsPending(); + +private: + QXcbEventNode *qXcbEventNodeFactory(xcb_generic_event_t *event); + void dequeueNode(); + + void sendCloseConnectionEvent() const; + bool isCloseConnectionEvent(const xcb_generic_event_t *event); + + QXcbEventNode *m_head = nullptr; + QXcbEventNode *m_flushedTail = nullptr; + std::atomic m_tail { nullptr }; + std::atomic_uint m_nodesRestored { 0 }; + + QXcbConnection *m_connection = nullptr; + bool m_closeConnectionDetected = false; + + uint m_freeNodes = PoolSize; + uint m_poolIndex = 0; + + qint32 m_peekerIdSource = 0; + bool m_queueModified = false; + bool m_peekerIndexCacheDirty = false; + QHash m_peekerToNode; + + // debug stats + quint64 m_nodesOnHeap = 0; +}; + +template +xcb_generic_event_t *QXcbEventQueue::peek(PeekOption option, Peeker &&peeker) +{ + flushBufferedEvents(); + if (isEmpty()) + return nullptr; + + QXcbEventNode *node = m_head; + do { + xcb_generic_event_t *event = node->event; + if (event && peeker(event, event->response_type & ~0x80)) { + if (option == PeekRemoveMatch || option == PeekRemoveMatchContinue) + node->event = nullptr; + if (option != PeekRemoveMatchContinue) + return event; + } + if (node == m_flushedTail) + break; + node = node->next; + } while (true); + + return nullptr; +} + +QT_END_NAMESPACE + +#endif -- cgit v1.2.3