I realize that this may be a novice GLib question but I've had a hard time finding code examples that solve my problem below. So I'd like your recommendations before I head too far down a wrong path.
My code listens for D-Bus messages. One D-Bus message is a "Quit" message that is intended to instruct the main loop to shut down. If there were no other tasks in the main loop, a simple solution could be to instead simply call g_main_loop_run()
in the code below and have the D-Bus message handling code (not shown here) execute g_main_loop_quit()
when the "Quit" message is received.
However, I prefer that the decision to quit be made by main loop, which in turn may perform a variety of other tasks in addition to listening for D-Bus messages. The following code does this as expected after setting up a working D-Bus server:
GMainLoop *glib_mainloop = g_main_loop_new( NULL, false );
/* Set up the D-Bus connection to work in the GLib event loop. */
dbus_connection_setup_with_g_main( dbus_connection, NULL );
/* Run the GLib event loop. */
GMainContext *glib_context = g_main_context_default( );
while( quit_indicator == false )
{
g_main_context_iteration( glib_context, /*allow blocking=*/false );
/* Do a variety of other tasks. */
}
g_main_loop_quit( glib_mainloop );
Note: the above is a bare minimum of code to illustrate my question and I'm aware that the "other tasks" in the main loop may be better handled by threads, GSource
s, or other means. For example, there would be obvious busy-waiting or timing issues if the above code were used as is.
My question is: is the above code sample a proper way to accomplish what I want or is there a more "true" GLib way of doing it?
Your approach is basically correct. A lot of examples say to use g_main_loop_run()
and g_main_loop_quit()
to control the main context, but it’s clearer to iterate the main context manually as you are doing.
One change which should be made to your code is to tell g_main_context_iteration()
to allow blocking, otherwise your main loop is essentially a busy loop, and your process will not sleep while waiting for I/O. You also don’t need to use a GMainLoop
at all if you iterate the GMainContext
manually.
Another necessary change is to call g_main_context_wakeup()
to wake up the main context from blocking in g_main_context_iteration()
when you change the value of the termination condition.
Thirdly, creating and quitting a GMainLoop
with g_main_loop_new()
/g_main_loop_quit()
does nothing in your code, since that GMainLoop
is never run with g_main_loop_run()
. Drop those calls.
Here’s an updated version:
GMainContext *context = g_main_context_default ();
gboolean quit = FALSE;
/* Set up the D-Bus connection to work in the GLib event loop. */
dbus_connection_setup_with_g_main (dbus_connection, context);
/* Run the GLib event loop. */
while (!quit)
g_main_context_iteration (context, TRUE);
/* To exit the main loop (from a callback implementing your D-Bus Quit() function): */
quit = TRUE;
g_main_context_wakeup (NULL /* global default main context */);
A few other points:
GMainContext
with g_autoptr(GMainContext) context = g_main_context_new (); g_main_context_push_thread_default (context);
. You should not share a GMainContext
between multiple threads. While it is safe to do this, it is not performant.Here’s an MPL-licensed example of the main loop in a modern GLib-based daemon using these techniques: https://git.apertis.org/cgit/rhosydd.git/tree/libcroesor/service.c#n569
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With