Blame SOURCES/0030-Move-the-wayland-socket-polling-to-a-separate-event-.patch

e1c9ed
From 91d36a2497f3289996d788c8974583bccac3c842 Mon Sep 17 00:00:00 2001
e1c9ed
From: Adrien Faveraux <af@brain-networks.fr>
e1c9ed
Date: Fri, 26 Nov 2021 09:18:58 +0100
e1c9ed
Subject: [PATCH 30/40] Move the wayland socket polling to a separate event
e1c9ed
 thread
e1c9ed
e1c9ed
New event threads is introduced which calls poll() on the wayland fd,
e1c9ed
instead of relying on the event dispatcher by using the QSocketNotifier.
e1c9ed
This allows to call in the proper order the wl_display_prepare_read(),
e1c9ed
poll() and wl_display_read_events() functions.
e1c9ed
e1c9ed
One thread is responsible for the default queue; when needed, it emit
e1c9ed
a signal so that the main thread can dispatch the queue. Another thread
e1c9ed
is responsible for the dedicated queue for frame callbacks; this thread
e1c9ed
will dispatch events on the thread itself.
e1c9ed
e1c9ed
QWaylandWindow is updated to, instead of each window's dedicated event
e1c9ed
queue, use this queue for frame callbacks.
e1c9ed
e1c9ed
Co-authored-by: Ratchanan Srirattanamet <ratchanan@ubports.com>
e1c9ed
Task-number: QTBUG-66075
e1c9ed
Change-Id: Ibb33ad7f4193b866d1b8d7a0405a94d59dcad5eb
e1c9ed
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
e1c9ed
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
e1c9ed
(cherry picked from commit 92a7904d9651348b0c307e84251c8440c6f75b22)
e1c9ed
---
e1c9ed
 src/client/qwaylanddisplay.cpp     | 302 +++++++++++++++++++++--------
e1c9ed
 src/client/qwaylanddisplay_p.h     |  21 +-
e1c9ed
 src/client/qwaylandintegration.cpp |   4 +-
e1c9ed
 src/client/qwaylandwindow.cpp      |  34 +++-
e1c9ed
 src/client/qwaylandwindow_p.h      |   2 +-
e1c9ed
 5 files changed, 255 insertions(+), 108 deletions(-)
e1c9ed
e1c9ed
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
e1c9ed
index 0f75cb7e..a7ce280a 100644
e1c9ed
--- a/src/client/qwaylanddisplay.cpp
e1c9ed
+++ b/src/client/qwaylanddisplay.cpp
e1c9ed
@@ -85,10 +85,203 @@
e1c9ed
 
e1c9ed
 #include <errno.h>
e1c9ed
 
e1c9ed
+#include <tuple> // for std::tie
e1c9ed
+
e1c9ed
+static void checkWaylandError(struct wl_display *display)
e1c9ed
+{
e1c9ed
+    int ecode = wl_display_get_error(display);
e1c9ed
+    if ((ecode == EPIPE || ecode == ECONNRESET)) {
e1c9ed
+        // special case this to provide a nicer error
e1c9ed
+        qWarning("The Wayland connection broke. Did the Wayland compositor die?");
e1c9ed
+    } else {
e1c9ed
+        qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
e1c9ed
+    }
e1c9ed
+    _exit(1);
e1c9ed
+}
e1c9ed
+
e1c9ed
 QT_BEGIN_NAMESPACE
e1c9ed
 
e1c9ed
 namespace QtWaylandClient {
e1c9ed
 
e1c9ed
+class EventThread : public QThread
e1c9ed
+{
e1c9ed
+    Q_OBJECT
e1c9ed
+public:
e1c9ed
+    enum OperatingMode {
e1c9ed
+        EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread.
e1c9ed
+        SelfDispatch, // Dispatch the events inside this thread.
e1c9ed
+    };
e1c9ed
+
e1c9ed
+    EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue,
e1c9ed
+                OperatingMode mode)
e1c9ed
+        : m_fd(wl_display_get_fd(wl))
e1c9ed
+        , m_pipefd{ -1, -1 }
e1c9ed
+        , m_wldisplay(wl)
e1c9ed
+        , m_wlevqueue(ev_queue)
e1c9ed
+        , m_mode(mode)
e1c9ed
+        , m_reading(true)
e1c9ed
+        , m_quitting(false)
e1c9ed
+    {
e1c9ed
+        setObjectName(QStringLiteral("WaylandEventThread"));
e1c9ed
+    }
e1c9ed
+
e1c9ed
+    void readAndDispatchEvents()
e1c9ed
+    {
e1c9ed
+        /*
e1c9ed
+         * Dispatch pending events and flush the requests at least once. If the event thread
e1c9ed
+         * is not reading, try to call _prepare_read() to allow the event thread to poll().
e1c9ed
+         * If that fails, re-try dispatch & flush again until _prepare_read() is successful.
e1c9ed
+         *
e1c9ed
+         * This allow any call to readAndDispatchEvents() to start event thread's polling,
e1c9ed
+         * not only the one issued from event thread's waitForReading(), which means functions
e1c9ed
+         * called from dispatch_pending() can safely spin an event loop.
e1c9ed
+         */
e1c9ed
+        for (;;) {
e1c9ed
+            if (dispatchQueuePending() < 0) {
e1c9ed
+                checkWaylandError(m_wldisplay);
e1c9ed
+                return;
e1c9ed
+            }
e1c9ed
+
e1c9ed
+            wl_display_flush(m_wldisplay);
e1c9ed
+
e1c9ed
+            // We have to check if event thread is reading every time we dispatch
e1c9ed
+            // something, as that may recursively call this function.
e1c9ed
+            if (m_reading.loadAcquire())
e1c9ed
+                break;
e1c9ed
+
e1c9ed
+            if (prepareReadQueue() == 0) {
e1c9ed
+                QMutexLocker l(&m_mutex);
e1c9ed
+                m_reading.storeRelease(true);
e1c9ed
+                m_cond.wakeOne();
e1c9ed
+                break;
e1c9ed
+            }
e1c9ed
+        }
e1c9ed
+    }
e1c9ed
+
e1c9ed
+    void stop()
e1c9ed
+    {
e1c9ed
+        // We have to both write to the pipe and set the flag, as the thread may be
e1c9ed
+        // either in the poll() or waiting for _prepare_read().
e1c9ed
+        if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1)
e1c9ed
+            qWarning("Failed to write to the pipe: %s.", strerror(errno));
e1c9ed
+
e1c9ed
+        {
e1c9ed
+            QMutexLocker l(&m_mutex);
e1c9ed
+            m_quitting = true;
e1c9ed
+            m_cond.wakeOne();
e1c9ed
+        }
e1c9ed
+
e1c9ed
+        wait();
e1c9ed
+    }
e1c9ed
+
e1c9ed
+Q_SIGNALS:
e1c9ed
+    void needReadAndDispatch();
e1c9ed
+
e1c9ed
+protected:
e1c9ed
+    void run() override
e1c9ed
+    {
e1c9ed
+        // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets
e1c9ed
+        // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore.
e1c9ed
+        struct Pipe
e1c9ed
+        {
e1c9ed
+            Pipe(int *fds)
e1c9ed
+                : fds(fds)
e1c9ed
+            {
e1c9ed
+                if (qt_safe_pipe(fds) != 0)
e1c9ed
+                    qWarning("Pipe creation failed. Quitting may hang.");
e1c9ed
+            }
e1c9ed
+            ~Pipe()
e1c9ed
+            {
e1c9ed
+                if (fds[0] != -1) {
e1c9ed
+                    close(fds[0]);
e1c9ed
+                    close(fds[1]);
e1c9ed
+                }
e1c9ed
+            }
e1c9ed
+
e1c9ed
+            int *fds;
e1c9ed
+        } pipe(m_pipefd);
e1c9ed
+
e1c9ed
+        // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the
e1c9ed
+        // outbound ones. Wait until it's done before proceeding, unless we're told to quit.
e1c9ed
+        while (waitForReading()) {
e1c9ed
+            pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
e1c9ed
+            poll(fds, 2, -1);
e1c9ed
+
e1c9ed
+            if (fds[1].revents & POLLIN) {
e1c9ed
+                // we don't really care to read the byte that was written here since we're closing down
e1c9ed
+                wl_display_cancel_read(m_wldisplay);
e1c9ed
+                break;
e1c9ed
+            }
e1c9ed
+
e1c9ed
+            if (fds[0].revents & POLLIN)
e1c9ed
+                wl_display_read_events(m_wldisplay);
e1c9ed
+                // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop
e1c9ed
+                // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which
e1c9ed
+                // case we don't care anymore about them.
e1c9ed
+            else
e1c9ed
+                wl_display_cancel_read(m_wldisplay);
e1c9ed
+        }
e1c9ed
+    }
e1c9ed
+
e1c9ed
+private:
e1c9ed
+    bool waitForReading()
e1c9ed
+    {
e1c9ed
+        Q_ASSERT(QThread::currentThread() == this);
e1c9ed
+
e1c9ed
+        m_reading.storeRelease(false);
e1c9ed
+
e1c9ed
+        if (m_mode == SelfDispatch) {
e1c9ed
+            readAndDispatchEvents();
e1c9ed
+        } else {
e1c9ed
+            Q_EMIT needReadAndDispatch();
e1c9ed
+
e1c9ed
+            QMutexLocker lock(&m_mutex);
e1c9ed
+            // m_reading might be set from our emit or some other invocation of
e1c9ed
+            // readAndDispatchEvents().
e1c9ed
+            while (!m_reading.loadRelaxed() && !m_quitting)
e1c9ed
+                m_cond.wait(&m_mutex);
e1c9ed
+        }
e1c9ed
+
e1c9ed
+        return !m_quitting;
e1c9ed
+    }
e1c9ed
+
e1c9ed
+    int dispatchQueuePending()
e1c9ed
+    {
e1c9ed
+        if (m_wlevqueue)
e1c9ed
+            return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
e1c9ed
+        else
e1c9ed
+            return wl_display_dispatch_pending(m_wldisplay);
e1c9ed
+    }
e1c9ed
+
e1c9ed
+    int prepareReadQueue()
e1c9ed
+    {
e1c9ed
+        if (m_wlevqueue)
e1c9ed
+            return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
e1c9ed
+        else
e1c9ed
+            return wl_display_prepare_read(m_wldisplay);
e1c9ed
+    }
e1c9ed
+
e1c9ed
+    int m_fd;
e1c9ed
+    int m_pipefd[2];
e1c9ed
+    wl_display *m_wldisplay;
e1c9ed
+    wl_event_queue *m_wlevqueue;
e1c9ed
+    OperatingMode m_mode;
e1c9ed
+
e1c9ed
+    /* Concurrency note when operating in EmitToDispatch mode:
e1c9ed
+     * m_reading is set to false inside event thread's waitForReading(), and is
e1c9ed
+     * set to true inside main thread's readAndDispatchEvents().
e1c9ed
+     * The lock is not taken when setting m_reading to false, as the main thread
e1c9ed
+     * is not actively waiting for it to turn false. However, the lock is taken
e1c9ed
+     * inside readAndDispatchEvents() before setting m_reading to true,
e1c9ed
+     * as the event thread is actively waiting for it under the wait condition.
e1c9ed
+     */
e1c9ed
+
e1c9ed
+    QAtomicInteger<bool> m_reading;
e1c9ed
+    bool m_quitting;
e1c9ed
+    QMutex m_mutex;
e1c9ed
+    QWaitCondition m_cond;
e1c9ed
+};
e1c9ed
+
e1c9ed
 Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
e1c9ed
 
e1c9ed
 struct wl_surface *QWaylandDisplay::createSurface(void *handle)
e1c9ed
@@ -162,6 +355,12 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
e1c9ed
 
e1c9ed
 QWaylandDisplay::~QWaylandDisplay(void)
e1c9ed
 {
e1c9ed
+    if (m_eventThread)
e1c9ed
+        m_eventThread->stop();
e1c9ed
+
e1c9ed
+    if (m_frameEventQueueThread)
e1c9ed
+        m_frameEventQueueThread->stop();
e1c9ed
+
e1c9ed
     if (mSyncCallback)
e1c9ed
         wl_callback_destroy(mSyncCallback);
e1c9ed
 
e1c9ed
@@ -208,98 +407,37 @@ void QWaylandDisplay::ensureScreen()
e1c9ed
 
e1c9ed
 void QWaylandDisplay::checkError() const
e1c9ed
 {
e1c9ed
-    int ecode = wl_display_get_error(mDisplay);
e1c9ed
-    if ((ecode == EPIPE || ecode == ECONNRESET)) {
e1c9ed
-        // special case this to provide a nicer error
e1c9ed
-        qWarning("The Wayland connection broke. Did the Wayland compositor die?");
e1c9ed
-    } else {
e1c9ed
-        qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
e1c9ed
-    }
e1c9ed
-    _exit(1);
e1c9ed
+    checkWaylandError(mDisplay);
e1c9ed
 }
e1c9ed
 
e1c9ed
+// Called in main thread, either from queued signal or directly.
e1c9ed
 void QWaylandDisplay::flushRequests()
e1c9ed
 {
e1c9ed
-    if (wl_display_prepare_read(mDisplay) == 0) {
e1c9ed
-        wl_display_read_events(mDisplay);
e1c9ed
-    }
e1c9ed
-
e1c9ed
-    if (wl_display_dispatch_pending(mDisplay) < 0)
e1c9ed
-        checkError();
e1c9ed
-
e1c9ed
-    {
e1c9ed
-        QReadLocker locker(&m_frameQueueLock);
e1c9ed
-        for (const FrameQueue &q : mExternalQueues) {
e1c9ed
-            QMutexLocker locker(q.mutex);
e1c9ed
-            while (wl_display_prepare_read_queue(mDisplay, q.queue) != 0)
e1c9ed
-                wl_display_dispatch_queue_pending(mDisplay, q.queue);
e1c9ed
-            wl_display_read_events(mDisplay);
e1c9ed
-            wl_display_dispatch_queue_pending(mDisplay, q.queue);
e1c9ed
-        }
e1c9ed
-    }
e1c9ed
-
e1c9ed
-    wl_display_flush(mDisplay);
e1c9ed
-}
e1c9ed
-
e1c9ed
-void QWaylandDisplay::blockingReadEvents()
e1c9ed
-{
e1c9ed
-    if (wl_display_dispatch(mDisplay) < 0)
e1c9ed
-        checkError();
e1c9ed
-}
e1c9ed
-
e1c9ed
-void QWaylandDisplay::destroyFrameQueue(const QWaylandDisplay::FrameQueue &q)
e1c9ed
-{
e1c9ed
-    QWriteLocker locker(&m_frameQueueLock);
e1c9ed
-    auto it = std::find_if(mExternalQueues.begin(),
e1c9ed
-                           mExternalQueues.end(),
e1c9ed
-                           [&q] (const QWaylandDisplay::FrameQueue &other){ return other.queue == q.queue; });
e1c9ed
-    Q_ASSERT(it != mExternalQueues.end());
e1c9ed
-    mExternalQueues.erase(it);
e1c9ed
-    if (q.queue != nullptr)
e1c9ed
-        wl_event_queue_destroy(q.queue);
e1c9ed
-    delete q.mutex;
e1c9ed
+    m_eventThread->readAndDispatchEvents();
e1c9ed
 }
e1c9ed
 
e1c9ed
-QWaylandDisplay::FrameQueue QWaylandDisplay::createFrameQueue()
e1c9ed
+// We have to wait until we have an eventDispatcher before creating the eventThread,
e1c9ed
+// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
e1c9ed
+// polling.
e1c9ed
+void QWaylandDisplay::initEventThread()
e1c9ed
 {
e1c9ed
-    QWriteLocker locker(&m_frameQueueLock);
e1c9ed
-    FrameQueue q{createEventQueue()};
e1c9ed
-    mExternalQueues.append(q);
e1c9ed
-    return q;
e1c9ed
-}
e1c9ed
+    m_eventThread.reset(
e1c9ed
+            new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch));
e1c9ed
+    connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this,
e1c9ed
+            &QWaylandDisplay::flushRequests, Qt::QueuedConnection);
e1c9ed
+    m_eventThread->start();
e1c9ed
 
e1c9ed
-wl_event_queue *QWaylandDisplay::createEventQueue()
e1c9ed
-{
e1c9ed
-    return wl_display_create_queue(mDisplay);
e1c9ed
+    // wl_display_disconnect() free this.
e1c9ed
+    m_frameEventQueue = wl_display_create_queue(mDisplay);
e1c9ed
+    m_frameEventQueueThread.reset(
e1c9ed
+            new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch));
e1c9ed
+    m_frameEventQueueThread->start();
e1c9ed
 }
e1c9ed
 
e1c9ed
-void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bool ()> condition, int timeout)
e1c9ed
+void QWaylandDisplay::blockingReadEvents()
e1c9ed
 {
e1c9ed
-    if (!condition())
e1c9ed
-        return;
e1c9ed
-
e1c9ed
-    QElapsedTimer timer;
e1c9ed
-    timer.start();
e1c9ed
-    struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN);
e1c9ed
-    while (timeout == -1 || timer.elapsed() < timeout) {
e1c9ed
-        while (wl_display_prepare_read_queue(mDisplay, queue) != 0)
e1c9ed
-            wl_display_dispatch_queue_pending(mDisplay, queue);
e1c9ed
-
e1c9ed
-        wl_display_flush(mDisplay);
e1c9ed
-
e1c9ed
-        const int remaining = qMax(timeout - timer.elapsed(), 0ll);
e1c9ed
-        const int pollTimeout = timeout == -1 ? -1 : remaining;
e1c9ed
-        if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0)
e1c9ed
-            wl_display_read_events(mDisplay);
e1c9ed
-        else
e1c9ed
-            wl_display_cancel_read(mDisplay);
e1c9ed
-
e1c9ed
-        if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0)
e1c9ed
-            checkError();
e1c9ed
-
e1c9ed
-        if (!condition())
e1c9ed
-            break;
e1c9ed
-    }
e1c9ed
+    if (wl_display_dispatch(mDisplay) < 0)
e1c9ed
+        checkWaylandError(mDisplay);
e1c9ed
 }
e1c9ed
 
e1c9ed
 QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
e1c9ed
@@ -674,4 +812,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p
e1c9ed
 
e1c9ed
 } // namespace QtWaylandClient
e1c9ed
 
e1c9ed
+#include "qwaylanddisplay.moc"
e1c9ed
+
e1c9ed
 QT_END_NAMESPACE
e1c9ed
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
e1c9ed
index d9c8849f..42bc661d 100644
e1c9ed
--- a/src/client/qwaylanddisplay_p.h
e1c9ed
+++ b/src/client/qwaylanddisplay_p.h
e1c9ed
@@ -109,6 +109,7 @@ class QWaylandSurface;
e1c9ed
 class QWaylandShellIntegration;
e1c9ed
 class QWaylandCursor;
e1c9ed
 class QWaylandCursorTheme;
e1c9ed
+class EventThread;
e1c9ed
 
e1c9ed
 typedef void (*RegistryListener)(void *data,
e1c9ed
                                  struct wl_registry *registry,
e1c9ed
@@ -120,12 +121,6 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandDisplay : public QObject, public QtWayland
e1c9ed
     Q_OBJECT
e1c9ed
 
e1c9ed
 public:
e1c9ed
-    struct FrameQueue {
e1c9ed
-        FrameQueue(wl_event_queue *q = nullptr) : queue(q), mutex(new QMutex) {}
e1c9ed
-        wl_event_queue *queue;
e1c9ed
-        QMutex *mutex;
e1c9ed
-    };
e1c9ed
-
e1c9ed
     QWaylandDisplay(QWaylandIntegration *waylandIntegration);
e1c9ed
     ~QWaylandDisplay(void) override;
e1c9ed
 
e1c9ed
@@ -212,12 +207,11 @@ public:
e1c9ed
     void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice);
e1c9ed
     void handleWindowDestroyed(QWaylandWindow *window);
e1c9ed
 
e1c9ed
-    wl_event_queue *createEventQueue();
e1c9ed
-    FrameQueue createFrameQueue();
e1c9ed
-    void destroyFrameQueue(const FrameQueue &q);
e1c9ed
-    void dispatchQueueWhile(wl_event_queue *queue, std::function<bool()> condition, int timeout = -1);
e1c9ed
+    wl_event_queue *frameEventQueue() { return m_frameEventQueue; };
e1c9ed
 
e1c9ed
     bool isKeyboardAvailable() const;
e1c9ed
+
e1c9ed
+    void initEventThread();
e1c9ed
 public slots:
e1c9ed
     void blockingReadEvents();
e1c9ed
     void flushRequests();
e1c9ed
@@ -240,6 +234,9 @@ private:
e1c9ed
     };
e1c9ed
 
e1c9ed
     struct wl_display *mDisplay = nullptr;
e1c9ed
+    QScopedPointer<EventThread> m_eventThread;
e1c9ed
+    wl_event_queue *m_frameEventQueue = nullptr;
e1c9ed
+    QScopedPointer<EventThread> m_frameEventQueueThread;
e1c9ed
     QtWayland::wl_compositor mCompositor;
e1c9ed
     QScopedPointer<QWaylandShm> mShm;
e1c9ed
     QList<QWaylandScreen *> mWaitingScreens;
e1c9ed
@@ -276,11 +273,9 @@ private:
e1c9ed
     QWaylandInputDevice *mLastInputDevice = nullptr;
e1c9ed
     QPointer<QWaylandWindow> mLastInputWindow;
e1c9ed
     QPointer<QWaylandWindow> mLastKeyboardFocus;
e1c9ed
-    QVector<QWaylandWindow *> mActiveWindows;
e1c9ed
-    QVector<FrameQueue> mExternalQueues;
e1c9ed
+    QList<QWaylandWindow *> mActiveWindows;
e1c9ed
     struct wl_callback *mSyncCallback = nullptr;
e1c9ed
     static const wl_callback_listener syncCallbackListener;
e1c9ed
-    QReadWriteLock m_frameQueueLock;
e1c9ed
 
e1c9ed
     bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull();
e1c9ed
 
e1c9ed
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
e1c9ed
index 3a6fa651..3b876047 100644
e1c9ed
--- a/src/client/qwaylandintegration.cpp
e1c9ed
+++ b/src/client/qwaylandintegration.cpp
e1c9ed
@@ -192,9 +192,7 @@ QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const
e1c9ed
 
e1c9ed
 void QWaylandIntegration::initialize()
e1c9ed
 {
e1c9ed
-    int fd = wl_display_get_fd(mDisplay->wl_display());
e1c9ed
-    QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data());
e1c9ed
-    QObject::connect(sn, SIGNAL(activated(QSocketDescriptor)), mDisplay.data(), SLOT(flushRequests()));
e1c9ed
+    mDisplay->initEventThread();
e1c9ed
 
e1c9ed
     // Call after eventDispatcher is fully connected, for QWaylandDisplay::forceRoundTrip()
e1c9ed
     mDisplay->initialize();
e1c9ed
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
e1c9ed
index 1597f67e..7de19a74 100644
e1c9ed
--- a/src/client/qwaylandwindow.cpp
e1c9ed
+++ b/src/client/qwaylandwindow.cpp
e1c9ed
@@ -76,7 +76,6 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
e1c9ed
 QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
e1c9ed
     : QPlatformWindow(window)
e1c9ed
     , mDisplay(display)
e1c9ed
-    , mFrameQueue(mDisplay->createFrameQueue())
e1c9ed
     , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
e1c9ed
 {
e1c9ed
     {
e1c9ed
@@ -95,8 +94,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
e1c9ed
 
e1c9ed
 QWaylandWindow::~QWaylandWindow()
e1c9ed
 {
e1c9ed
-    mDisplay->destroyFrameQueue(mFrameQueue);
e1c9ed
-
e1c9ed
     delete mWindowDecoration;
e1c9ed
 
e1c9ed
     if (mSurface)
e1c9ed
@@ -635,6 +632,8 @@ const wl_callback_listener QWaylandWindow::callbackListener = {
e1c9ed
 
e1c9ed
 void QWaylandWindow::handleFrameCallback()
e1c9ed
 {
e1c9ed
+    QMutexLocker locker(&mFrameSyncMutex);
e1c9ed
+
e1c9ed
     mWaitingForFrameCallback = false;
e1c9ed
     mFrameCallbackElapsedTimer.invalidate();
e1c9ed
 
e1c9ed
@@ -656,12 +655,16 @@ void QWaylandWindow::handleFrameCallback()
e1c9ed
         mWaitingForUpdateDelivery = true;
e1c9ed
         QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection);
e1c9ed
     }
e1c9ed
+
e1c9ed
+    mFrameSyncWait.notify_all();
e1c9ed
 }
e1c9ed
 
e1c9ed
 bool QWaylandWindow::waitForFrameSync(int timeout)
e1c9ed
 {
e1c9ed
-    QMutexLocker locker(mFrameQueue.mutex);
e1c9ed
-    mDisplay->dispatchQueueWhile(mFrameQueue.queue, [&]() { return mWaitingForFrameCallback; }, timeout);
e1c9ed
+    QMutexLocker locker(&mFrameSyncMutex);
e1c9ed
+
e1c9ed
+    QDeadlineTimer deadline(timeout);
e1c9ed
+    while (mWaitingForFrameCallback && mFrameSyncWait.wait(&mFrameSyncMutex, deadline)) { }
e1c9ed
 
e1c9ed
     if (mWaitingForFrameCallback) {
e1c9ed
         qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
e1c9ed
@@ -1157,8 +1160,11 @@ void QWaylandWindow::requestUpdate()
e1c9ed
     Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
e1c9ed
 
e1c9ed
     // If we have a frame callback all is good and will be taken care of there
e1c9ed
-    if (mWaitingForFrameCallback)
e1c9ed
-        return;
e1c9ed
+    {
e1c9ed
+        QMutexLocker locker(&mFrameSyncMutex);
e1c9ed
+        if (mWaitingForFrameCallback)
e1c9ed
+            return;
e1c9ed
+    }
e1c9ed
 
e1c9ed
     // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet
e1c9ed
     // This is a somewhat redundant behavior and might indicate a bug in the calling code, so log
e1c9ed
@@ -1171,7 +1177,12 @@ void QWaylandWindow::requestUpdate()
e1c9ed
     // so use invokeMethod to delay the delivery a bit.
e1c9ed
     QMetaObject::invokeMethod(this, [this] {
e1c9ed
         // Things might have changed in the meantime
e1c9ed
-        if (hasPendingUpdateRequest() && !mWaitingForFrameCallback)
e1c9ed
+        {
e1c9ed
+            QMutexLocker locker(&mFrameSyncMutex);
e1c9ed
+            if (mWaitingForFrameCallback)
e1c9ed
+                return;
e1c9ed
+        }
e1c9ed
+        if (hasPendingUpdateRequest())
e1c9ed
             deliverUpdateRequest();
e1c9ed
     }, Qt::QueuedConnection);
e1c9ed
 }
e1c9ed
@@ -1191,9 +1202,10 @@ void QWaylandWindow::handleUpdate()
e1c9ed
     if (!mSurface)
e1c9ed
         return;
e1c9ed
 
e1c9ed
-    QMutexLocker locker(mFrameQueue.mutex);
e1c9ed
+    QMutexLocker locker(&mFrameSyncMutex);
e1c9ed
+
e1c9ed
     struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
e1c9ed
-    wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mFrameQueue.queue);
e1c9ed
+    wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mDisplay->frameEventQueue());
e1c9ed
     mFrameCallback = wl_surface_frame(wrappedSurface);
e1c9ed
     wl_proxy_wrapper_destroy(wrappedSurface);
e1c9ed
     wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
e1c9ed
@@ -1203,6 +1215,8 @@ void QWaylandWindow::handleUpdate()
e1c9ed
     // Start a timer for handling the case when the compositor stops sending frame callbacks.
e1c9ed
     if (mFrameCallbackTimeout > 0) {
e1c9ed
         QMetaObject::invokeMethod(this, [this] {
e1c9ed
+            QMutexLocker locker(&mFrameSyncMutex);
e1c9ed
+
e1c9ed
             if (mWaitingForFrameCallback) {
e1c9ed
                 if (mFrameCallbackCheckIntervalTimerId < 0)
e1c9ed
                     mFrameCallbackCheckIntervalTimerId = startTimer(mFrameCallbackTimeout);
e1c9ed
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
e1c9ed
index e0687962..d45980a8 100644
e1c9ed
--- a/src/client/qwaylandwindow_p.h
e1c9ed
+++ b/src/client/qwaylandwindow_p.h
e1c9ed
@@ -232,7 +232,7 @@ protected:
e1c9ed
     int mFrameCallbackCheckIntervalTimerId = -1;
e1c9ed
     QElapsedTimer mFrameCallbackElapsedTimer;
e1c9ed
     struct ::wl_callback *mFrameCallback = nullptr;
e1c9ed
-    QWaylandDisplay::FrameQueue mFrameQueue;
e1c9ed
+    QMutex mFrameSyncMutex;
e1c9ed
     QWaitCondition mFrameSyncWait;
e1c9ed
 
e1c9ed
     // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
e1c9ed
-- 
e1c9ed
2.35.1
e1c9ed