forked from osmocom/wireshark
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.
This commit is contained in:
parent
5a8977acd2
commit
18e08d04d1
|
@ -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
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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 */
|
|
@ -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)),
|
||||
|
|
Loading…
Reference in New Issue