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

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