From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- ui/qt/glib_mainloop_on_qeventloop.cpp | 125 ++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 ui/qt/glib_mainloop_on_qeventloop.cpp (limited to 'ui/qt/glib_mainloop_on_qeventloop.cpp') diff --git a/ui/qt/glib_mainloop_on_qeventloop.cpp b/ui/qt/glib_mainloop_on_qeventloop.cpp new file mode 100644 index 00000000..f715bbc7 --- /dev/null +++ b/ui/qt/glib_mainloop_on_qeventloop.cpp @@ -0,0 +1,125 @@ +/** @file + * + * Copyright 2022 Tomasz Mon + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "glib_mainloop_on_qeventloop.h" + +GLibPoller::GLibPoller(GMainContext *context) : + mutex_(), dispatched_(), + ctx_(context), priority_(0), + fds_(g_new(GPollFD, 1)), allocated_fds_(1), nfds_(0) +{ + g_main_context_ref(ctx_); +} + +GLibPoller::~GLibPoller() +{ + g_main_context_unref(ctx_); + g_free(fds_); +} + +void GLibPoller::run() +{ + gint timeout; + + mutex_.lock(); + while (!isInterruptionRequested()) + { + while (!g_main_context_acquire(ctx_)) + { + /* In normal circumstances context is acquired right away */ + } + g_main_context_prepare(ctx_, &priority_); + while ((nfds_ = g_main_context_query(ctx_, priority_, &timeout, fds_, + allocated_fds_)) > allocated_fds_) + { + g_free(fds_); + fds_ = g_new(GPollFD, nfds_); + allocated_fds_ = nfds_; + } + /* Blocking g_poll() call is the reason for separate polling thread */ + g_poll(fds_, nfds_, timeout); + g_main_context_release(ctx_); + + /* Polling has finished, dispatch events (if any) in main thread so we + * don't have to worry about concurrency issues in GLib callbacks. + */ + emit polled(); + /* Wait for the main thread to finish dispatching before next poll */ + dispatched_.wait(&mutex_); + } + mutex_.unlock(); +} + +GLibMainloopOnQEventLoop::GLibMainloopOnQEventLoop(QObject *parent) : + QObject(parent), + poller_(g_main_context_default()) +{ + connect(&poller_, &GLibPoller::polled, + this, &GLibMainloopOnQEventLoop::checkAndDispatch); + poller_.setObjectName("GLibPoller"); + poller_.start(); +} + +GLibMainloopOnQEventLoop::~GLibMainloopOnQEventLoop() +{ + poller_.requestInterruption(); + /* Wakeup poller thread in case it is blocked on g_poll(). Wakeup does not + * cause any problem if poller thread is already waiting on dispatched wait + * condition. + */ + g_main_context_wakeup(poller_.ctx_); + /* Wakeup poller thread without actually dispatching */ + poller_.mutex_.lock(); + poller_.dispatched_.wakeOne(); + poller_.mutex_.unlock(); + /* Poller thread will quit, wait for it to avoid warning */ + poller_.wait(); +} + +void GLibMainloopOnQEventLoop::checkAndDispatch() +{ + poller_.mutex_.lock(); + while (!g_main_context_acquire(poller_.ctx_)) + { + /* In normal circumstances context is acquired right away */ + } + if (g_main_depth() > 0) + { + /* This should not happen, but if it does warn about nested event loops + * so the issue can be fixed before the harm is done. To identify root + * cause, put breakpoint here and take backtrace when it hits. Look for + * calls to exec() and processEvents() functions. Refactor the code so + * it does not spin additional event loops. + * + * Ignoring this warning will lead to very strange and hard to debug + * problems in the future. + */ + qWarning("Nested GLib event loop detected"); + } + if (g_main_context_check(poller_.ctx_, poller_.priority_, + poller_.fds_, poller_.nfds_)) + { + g_main_context_dispatch(poller_.ctx_); + } + g_main_context_release(poller_.ctx_); + /* Start next iteration in GLibPoller thread */ + poller_.dispatched_.wakeOne(); + poller_.mutex_.unlock(); +} + +void GLibMainloopOnQEventLoop::setup(QObject *parent) +{ + /* Schedule event loop action so we can check if Qt runs GLib mainloop */ + QTimer::singleShot(0, [parent]() { + if (g_main_depth() == 0) + { + /* Not running inside GLib mainloop, actually setup */ + new GLibMainloopOnQEventLoop(parent); + } + }); +} -- cgit v1.2.3