aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Moń <desowin@gmail.com>2022-07-23 19:03:30 +0200
committerTomasz Moń <desowin@gmail.com>2022-07-24 20:57:18 +0200
commit18e08d04d1f04f027fb2a911abfadb2117f8d10d (patch)
treec0be31e3842ac56a2c89b0d968638028dcbd3314
parent5a8977acd278616ddb8c6604d31355b8dd369c7d (diff)
Qt: Setup GLib mainloop when needed
GLib watches and timeouts require GLib mainloop iterations. If the GLib mainloop is not running, then GLib watches and timeouts won't trigger. Back in the GTK+ days, then GLib mainloop was running on all systems. Since the Qt transition, GLib mainloop only runs on Linux when Qt does support it and environment variable QT_NO_GLIB=1 is not set. Start polling GLib mainloop in separate thread if Qt is not running GLib mainloop. Note that only the polling is handled in separate thread, the dispatch and thus all user callbacks execute in the main thread. Running GLib mainloop when needed enables full GLib functionality on all platforms and thus allows us to simplify our code by using GLib platform specific code.
-rw-r--r--ui/logray/CMakeLists.txt2
-rw-r--r--ui/logray/logray_main.cpp3
-rw-r--r--ui/qt/CMakeLists.txt2
-rw-r--r--ui/qt/glib_mainloop_on_qeventloop.cpp125
-rw-r--r--ui/qt/glib_mainloop_on_qeventloop.h57
-rw-r--r--ui/qt/main.cpp3
6 files changed, 192 insertions, 0 deletions
diff --git a/ui/logray/CMakeLists.txt b/ui/logray/CMakeLists.txt
index 377386738b..db9d6e494d 100644
--- a/ui/logray/CMakeLists.txt
+++ b/ui/logray/CMakeLists.txt
@@ -175,6 +175,7 @@ set(WIRESHARK_QT_HEADERS
../qt/funnel_string_dialog.h
../qt/funnel_text_dialog.h
../qt/geometry_state_dialog.h
+ ../qt/glib_mainloop_on_qeventloop.h
../qt/import_text_dialog.h
../qt/interface_frame.h
../qt/interface_toolbar_reader.h
@@ -394,6 +395,7 @@ set(WIRESHARK_QT_SRC
../qt/funnel_string_dialog.cpp
../qt/funnel_text_dialog.cpp
../qt/geometry_state_dialog.cpp
+ ../qt/glib_mainloop_on_qeventloop.cpp
../qt/import_text_dialog.cpp
../qt/interface_frame.cpp
../qt/interface_toolbar_reader.cpp
diff --git a/ui/logray/logray_main.cpp b/ui/logray/logray_main.cpp
index 33ab7ed2a4..5288027ce0 100644
--- a/ui/logray/logray_main.cpp
+++ b/ui/logray/logray_main.cpp
@@ -81,6 +81,7 @@
#include "ui/qt/utils/color_utils.h"
#include "ui/qt/coloring_rules_dialog.h"
#include "ui/qt/endpoint_dialog.h"
+#include "ui/qt/glib_mainloop_on_qeventloop.h"
#include "ui/logray/logray_main_window.h"
#include "ui/qt/simple_dialog.h"
#include "ui/qt/simple_statistics_dialog.h"
@@ -687,6 +688,8 @@ int main(int argc, char *qt_argv[])
// Init the main window (and splash)
main_w = new(LograyMainWindow);
main_w->show();
+ // Setup GLib mainloop on Qt event loop to enable GLib and GIO watches
+ GLibMainloopOnQEventLoop::setup(main_w);
// We may not need a queued connection here but it would seem to make sense
// to force the issue.
main_w->connect(&ls_app, SIGNAL(openCaptureFile(QString,QString,unsigned int)),
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index e2a9f591bd..c13c08c14a 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -178,6 +178,7 @@ set(WIRESHARK_QT_HEADERS
funnel_string_dialog.h
funnel_text_dialog.h
geometry_state_dialog.h
+ glib_mainloop_on_qeventloop.h
gsm_map_summary_dialog.h
iax2_analysis_dialog.h
import_text_dialog.h
@@ -422,6 +423,7 @@ set(WIRESHARK_QT_SRC
funnel_string_dialog.cpp
funnel_text_dialog.cpp
geometry_state_dialog.cpp
+ glib_mainloop_on_qeventloop.cpp
iax2_analysis_dialog.cpp
import_text_dialog.cpp
interface_frame.cpp
diff --git a/ui/qt/glib_mainloop_on_qeventloop.cpp b/ui/qt/glib_mainloop_on_qeventloop.cpp
new file mode 100644
index 0000000000..f715bbc728
--- /dev/null
+++ b/ui/qt/glib_mainloop_on_qeventloop.cpp
@@ -0,0 +1,125 @@
+/** @file
+ *
+ * Copyright 2022 Tomasz Mon <desowin@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <QTimer>
+#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);
+ }
+ });
+}
diff --git a/ui/qt/glib_mainloop_on_qeventloop.h b/ui/qt/glib_mainloop_on_qeventloop.h
new file mode 100644
index 0000000000..9f66c68164
--- /dev/null
+++ b/ui/qt/glib_mainloop_on_qeventloop.h
@@ -0,0 +1,57 @@
+/** @file
+ *
+ * Copyright 2022 Tomasz Mon <desowin@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef GLIB_MAINLOOP_ON_QEVENTLOOP_H
+#define GLIB_MAINLOOP_ON_QEVENTLOOP_H
+
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+#include <glib.h>
+
+class GLibPoller : public QThread
+{
+ Q_OBJECT
+
+protected:
+ explicit GLibPoller(GMainContext *context);
+ ~GLibPoller();
+
+ void run() override;
+
+ QMutex mutex_;
+ QWaitCondition dispatched_;
+ GMainContext *ctx_;
+ gint priority_;
+ GPollFD *fds_;
+ gint allocated_fds_, nfds_;
+
+signals:
+ void polled(void);
+
+ friend class GLibMainloopOnQEventLoop;
+};
+
+class GLibMainloopOnQEventLoop : public QObject
+{
+ Q_OBJECT
+
+protected:
+ explicit GLibMainloopOnQEventLoop(QObject *parent);
+ ~GLibMainloopOnQEventLoop();
+
+protected slots:
+ void checkAndDispatch();
+
+public:
+ static void setup(QObject *parent);
+
+protected:
+ GLibPoller poller_;
+};
+
+#endif /* GLIB_MAINLOOP_ON_QEVENTLOOP_H */
diff --git a/ui/qt/main.cpp b/ui/qt/main.cpp
index f6a03bd05c..9b2c3cb31b 100644
--- a/ui/qt/main.cpp
+++ b/ui/qt/main.cpp
@@ -81,6 +81,7 @@
#include "ui/qt/utils/color_utils.h"
#include "ui/qt/coloring_rules_dialog.h"
#include "ui/qt/endpoint_dialog.h"
+#include "ui/qt/glib_mainloop_on_qeventloop.h"
#include "ui/qt/wireshark_main_window.h"
#include "ui/qt/response_time_delay_dialog.h"
#include "ui/qt/service_response_time_dialog.h"
@@ -689,6 +690,8 @@ int main(int argc, char *qt_argv[])
// Init the main window (and splash)
main_w = new(WiresharkMainWindow);
main_w->show();
+ // Setup GLib mainloop on Qt event loop to enable GLib and GIO watches
+ GLibMainloopOnQEventLoop::setup(main_w);
// We may not need a queued connection here but it would seem to make sense
// to force the issue.
main_w->connect(&ws_app, SIGNAL(openCaptureFile(QString,QString,unsigned int)),