Asynchronous Callbacks not being called for nmlib async functions

Related to this thread: Asynchronous method/callback example?

For example, I’m calling

 // ... some setup logic
  nm_client_add_and_activate_connection_async(client, connection, device, NULL, NULL, (GAsyncReadyCallback) add_connection_cb, &uuid);
  return uuid; // this returns a response to an http request so that the user can check the status later

where add_connection_cb is defined as:

static void add_connection_cb(NMClient *client, GAsyncResult *result, gpointer user_data) {
  // not getting here, womp womp
}

If I run the function synchronously with an in-function g_main_loop_run, it works just dandy, except for the fact that it’s synchronous. Does this have to be synchronous to call? I’m not sure what’s going wrong. I’ve also tried supplying NULL to user_data in case it didn’t like that, but still didn’t have success.

static void
add_connection_cb(NMClient     *client,
                  GAsyncResult *result,
                  gpointer      user_data)
{
 /* This callback will be invoked in the same thread as
  * nm_client_add_and_activate_connection_async(), once
  * the task completes.
  */
}

/* Calling this function gathers the arguments into a
 * task object and passes it to a task thread. When
 * the task completes, the callback you passed is
 * invoked in _this_ thread's main context.
 */
nm_client_add_and_activate_connection_async(client,
                                            connection,
                                            device,
                                            NULL,
                                            NULL,
                                            (GAsyncReadyCallback) add_connection_cb,
                                            &uuid)

The GLib main context runs a function and when it completes, control is returned to the main context, which then runs the next function. This allows for a sort of cooperateive-scheduling, either by delegating blocking work to another thread, or by deferring with an event source as created by e.g. g_timeout_add() or g_idle_add().

Asynchronous functions work in GIO by gathering task data (i.e. the function arguments) into a GTask object, including a reference to the current GMainContext and the callback you provided. When the task completes, it schedules the callback (i.e add_connection_cb()) to be invoked in the original main context using some event source.

Using a GMainLoop to iterate the main context until the task completes, is in fact a common way to make asynchronous-only methods into synchronous ones. Without seeing more of your code, what you probably want to do is pass your self object (maybe that’s a GtkWidget) as the user_data argument. Then your callback will look something like:

static void
add_connection_cb(NMClient     *client,
                  GAsyncResult *result,
                  GtkWidget    *self)
{
  g_autoptr (NMActiveConnection) conn = NULL;
  g_autoptr (GError) error = NULL;

  conn = nm_client_add_and_activate_connection_finish (client,
                                                       result,
                                                       &error);

  if (error != NULL)
    {
      g_warning ("%s(): %s", G_STRFUNC, error->message);
      return;
    }

  /* The operation succeeded, so do something with the object
   * and add some notification to the GUI.
   */
  g_set_object (&self->conn, conn);
  gtk_label_set_label (self->status_label, "Succeeded!");
}

Thanks for the quick response!

Does this have to been done with a g_main_loop and its context, or can I apply the same logic to a libevent application?

For more context, currently I’m not using GMainContext at all, and just libevent in main. I can provide the libevent loop (or an event?) to the module if needed, just not sure if I can use it for this purpose.

If I have to use GMainContext/etc here, how would I do that (I don’t have a GUI, so I’m not sure that I could use GtkWidget). I’m thinking:

GMainLoop loop;
void my_module_init(void) { // initialize this in the main application
  loop = g_main_loop_new(NULL, FALSE);
  // any setup
  g_main_loop_run(loop)
}

std::string my_http_add_network_cxn_function(char *ssid, char *pass) {
  // configure client, connection, device
 // set UUID to network ID
  g_timeout_add() or g_idle_add() // what would I put in one of these? is this where I call `nm_client_add_and_activate_connection` with my callback?
  return uuid;
}

I’m pretty new to glib/gobject and these functions in general so I appreciate the help!

I’m not familiar with libevent, but I would guess you’d have to integrate it into GLib’s main context (see Customizing the main loop iteration).

It’s not important whether you have a GUI, but to pass something to the async function’s user_data argument that can be used from the callback:

static void
add_connection_cb (NMClient     *client,
                   GAsyncResult *result,
                   SomeData     *myData);            // <-- user_data argument

nm_client_add_and_activate_connection_async(client,
                                            connection,
                                            device,
                                            NULL,
                                            NULL,
                                            (GAsyncReadyCallback)add_connection_cb,
                                            myData); // <-- user_data argument

When nm_client_add_and_activate_connection_async() returns, which is almost immediately, it returns control to the main context (assuming there is one). When the task is completes, it will add an event source with the callback to the main context.

You must therefore iterate the main context if you want that callback to ever be invoked. You can use g_idle_add() as a simple way to add functions to the GMainContext, but you will have add at least one before you call g_main_loop_run(), because that function will never return until g_main_loop_quit() is called from somewhere.

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