Calling g_main_loop_quit from a signal handler

Should I be able to call g_main_loop_quit from a signal handler?

I have a GObject subclass, let’s call XQuitter. This class defines a signal named “disconnected”. I use an instance of this subclass in a program that uses a GMainLoop to maintain a connection to a D-Bus broker. The program connects to the broker using g_bus_own_name. The instance is passed into this function as the user_data parameter. In the name_lost_handler function, g_signal_emit is called using the instance as the object and “disconnected” as the signal name. In the signal handler callback function, the GMainLoop is passed in as the user_data parameter, and the function g_main_loop_quit is called. My assumption is that the main loop will quit running and the program will exit cleanly. However, the signal handler function is called, but the program does not quit. Is this expected behavior? Am I misusing GObject signals? Is there a better way to have my program quit if it cannot acquire a name on the bus?

I believe I have isolated this behavior into a small C program.

#include <gio/gio.h>
#include <glib-object.h>

#define X_TYPE_QUITTER (x_quitter_get_type())
G_DECLARE_FINAL_TYPE (XQuitter, x_quitter, X, QUITTER, GObject)

struct _XQuitter
{
    GObject parent_instance;
};

G_DEFINE_FINAL_TYPE (XQuitter, x_quitter, G_TYPE_OBJECT)

enum {
    SIGNAL_DISCONNECTED = 1,
    N_SIGNALS
};
static guint signals[N_SIGNALS];

static void
on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data)
{
    g_debug ("on_bus_acquired: %s", name);
}

static void
on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data)
{
    g_debug ("on_name_acquired: %s", name);
}

static void
on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data)
{
    g_debug ("on_name_lost: %s", name);

    XQuitter *self = X_QUITTER (user_data);
    g_signal_emit (self, signals[SIGNAL_DISCONNECTED], 0);
}

static void
x_quitter_class_init (XQuitterClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    signals[SIGNAL_DISCONNECTED] = g_signal_newv ("disconnected",
                                                  G_TYPE_FROM_CLASS (object_class),
                                                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                                                  NULL,
                                                  NULL,
                                                  NULL,
                                                  NULL,
                                                  G_TYPE_NONE,
                                                  0,
                                                  NULL);
}

static void
x_quitter_init (XQuitter *self)
{

}

static void
disconnected_signal_handler (gpointer user_data)
{
    g_debug ("disconnected_signal_handler");
    g_main_loop_quit (user_data);
}

gint
main (gint argc, gchar *argv[])
{
    g_autoptr (GMainContext) context = g_main_context_default ();
    g_autoptr (GMainLoop) loop = g_main_loop_new (context, FALSE);
    g_autoptr (XQuitter) quitter = g_object_new (X_TYPE_QUITTER, NULL);
    g_signal_connect (quitter, "disconnected", G_CALLBACK (disconnected_signal_handler), loop);
    guint bus_id = g_bus_own_name (G_BUS_TYPE_STARTER,
                                   "com.test.quitter",
                                   G_BUS_NAME_OWNER_FLAGS_NONE,
                                   on_bus_acquired,
                                   on_name_acquired,
                                   on_name_lost,
                                   quitter,
                                   NULL);
    g_main_loop_run (loop);

    return 0;
}

The first parameter passed to the signal handler is the object that emitted the signal (which doesn’t seem to be mentioned in the GObject docs). So your signal handler signature should be:

static void
disconnected_signal_handler (XQuitter *instance, gpointer user_data);

Your compiler may complain that instance is unused. You can do (void)instance; in the beginning of the handler to silence it, or use G_GNUC_UNUSED.

Thank you! Changing the signal handler signature to include the emitting object as the first parameter solved the issue.

It’s supposed to work.

I think the parameters to your signal handler are wrong. Try this:

static void
disconnected_signal_handler (XQuitter *quitter,
                             gpointer  user_data)
{
    g_debug ("disconnected_signal_handler");
    g_main_loop_quit (user_data);
}

I see Neui already figured out the problem. Shame that Discourse doesn’t show updated comments on posts. This wastes everybody’s time. :frowning:

Good spot. MR to improve the docs here: docs: Minor improvements to GSignal documentation (!3956) · Merge requests · GNOME / GLib · GitLab

Further improvements welcome :slight_smile:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.