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

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