3 * Copyright 2022 Tomasz Mon <desowin@gmail.com>
5 * SPDX-License-Identifier: GPL-2.0-or-later
11 #include "glib_mainloop_on_qeventloop.h"
13 GLibPoller::GLibPoller(GMainContext
*context
) :
14 mutex_(), dispatched_(),
15 ctx_(context
), priority_(0),
16 fds_(g_new(GPollFD
, 1)), allocated_fds_(1), nfds_(0)
18 g_main_context_ref(ctx_
);
21 GLibPoller::~GLibPoller()
23 g_main_context_unref(ctx_
);
27 void GLibPoller::run()
32 while (!isInterruptionRequested())
34 while (!g_main_context_acquire(ctx_
))
36 /* In normal circumstances context is acquired right away */
38 g_main_context_prepare(ctx_
, &priority_
);
39 while ((nfds_
= g_main_context_query(ctx_
, priority_
, &timeout
, fds_
,
40 allocated_fds_
)) > allocated_fds_
)
43 fds_
= g_new(GPollFD
, nfds_
);
44 allocated_fds_
= nfds_
;
46 /* Blocking g_poll() call is the reason for separate polling thread */
47 g_poll(fds_
, nfds_
, timeout
);
48 g_main_context_release(ctx_
);
50 /* Polling has finished, dispatch events (if any) in main thread so we
51 * don't have to worry about concurrency issues in GLib callbacks.
54 /* Wait for the main thread to finish dispatching before next poll */
55 dispatched_
.wait(&mutex_
);
60 GLibMainloopOnQEventLoop::GLibMainloopOnQEventLoop(QObject
*parent
) :
62 poller_(g_main_context_default())
64 connect(&poller_
, &GLibPoller::polled
,
65 this, &GLibMainloopOnQEventLoop::checkAndDispatch
);
66 poller_
.setObjectName("GLibPoller");
70 GLibMainloopOnQEventLoop::~GLibMainloopOnQEventLoop()
72 poller_
.requestInterruption();
73 /* Wakeup poller thread in case it is blocked on g_poll(). Wakeup does not
74 * cause any problem if poller thread is already waiting on dispatched wait
77 g_main_context_wakeup(poller_
.ctx_
);
78 /* Wakeup poller thread without actually dispatching */
79 poller_
.mutex_
.lock();
80 poller_
.dispatched_
.wakeOne();
81 poller_
.mutex_
.unlock();
82 /* Poller thread will quit, wait for it to avoid warning */
86 void GLibMainloopOnQEventLoop::checkAndDispatch()
88 poller_
.mutex_
.lock();
89 while (!g_main_context_acquire(poller_
.ctx_
))
91 /* In normal circumstances context is acquired right away */
93 if (g_main_depth() > 0)
95 /* This should not happen, but if it does warn about nested event loops
96 * so the issue can be fixed before the harm is done. To identify root
97 * cause, put breakpoint here and take backtrace when it hits. Look for
98 * calls to exec() and processEvents() functions. Refactor the code so
99 * it does not spin additional event loops.
101 * Ignoring this warning will lead to very strange and hard to debug
102 * problems in the future.
104 qWarning("Nested GLib event loop detected");
106 if (g_main_context_check(poller_
.ctx_
, poller_
.priority_
,
107 poller_
.fds_
, poller_
.nfds_
))
109 g_main_context_dispatch(poller_
.ctx_
);
111 g_main_context_release(poller_
.ctx_
);
112 /* Start next iteration in GLibPoller thread */
113 poller_
.dispatched_
.wakeOne();
114 poller_
.mutex_
.unlock();
117 void GLibMainloopOnQEventLoop::setup(QObject
*parent
)
119 /* Schedule event loop action so we can check if Qt runs GLib mainloop */
120 QTimer::singleShot(0, [parent
]() {
121 if (g_main_depth() == 0)
123 /* Not running inside GLib mainloop, actually setup */
124 new GLibMainloopOnQEventLoop(parent
);