[gnome-online-accounts] provider: make goa_provider_get_all() async



commit 1a8bfdf90fd24e9bbaeeae15ff9f16e847a6b48e
Author: Marco Barisione <marco barisione collabora co uk>
Date:   Wed Jul 24 16:32:55 2013 +0100

    provider: make goa_provider_get_all() async
    
    https://bugzilla.gnome.org/show_bug.cgi?id=696267

 src/daemon/goadaemon.c        |  121 +++++++++++++++++++---------
 src/examples/list-providers.c |   41 +++++++++-
 src/goabackend/goaprovider.c  |  176 ++++++++++++++++++++++++++++++++++++++---
 src/goabackend/goaprovider.h  |    6 +-
 4 files changed, 292 insertions(+), 52 deletions(-)
---
diff --git a/src/daemon/goadaemon.c b/src/daemon/goadaemon.c
index aead6b1..5da951d 100644
--- a/src/daemon/goadaemon.c
+++ b/src/daemon/goadaemon.c
@@ -717,17 +717,24 @@ generate_new_id (GoaDaemon *daemon)
   return ret;
 }
 
-static gboolean
-on_manager_handle_add_account (GoaManager             *manager,
-                               GDBusMethodInvocation  *invocation,
-                               const gchar            *provider_type,
-                               const gchar            *identity,
-                               const gchar            *presentation_identity,
-                               GVariant               *credentials,
-                               GVariant               *details,
-                               gpointer                user_data)
+typedef struct
 {
-  GoaDaemon *daemon = GOA_DAEMON (user_data);
+  GoaDaemon *daemon;
+  GoaManager *manager;
+  GDBusMethodInvocation *invocation;
+  gchar *provider_type;
+  gchar *identity;
+  gchar *presentation_identity;
+  GVariant *credentials;
+  GVariant *details;
+} AddAccountData;
+
+static void
+get_all_providers_cb (GObject      *source,
+                      GAsyncResult *res,
+                      gpointer      user_data)
+{
+  AddAccountData *data = user_data;
   GoaProvider *provider;
   GKeyFile *key_file;
   GError *error;
@@ -736,7 +743,7 @@ on_manager_handle_add_account (GoaManager             *manager,
   gchar *path;
   gchar *id;
   gchar *group;
-  gchar *data;
+  gchar *key_file_data;
   gsize length;
   gchar *object_path;
   GVariantIter iter;
@@ -751,10 +758,12 @@ on_manager_handle_add_account (GoaManager             *manager,
   path = NULL;
   id = NULL;
   group = NULL;
-  data = NULL;
+  key_file_data = NULL;
   object_path = NULL;
 
-  providers = goa_provider_get_all ();
+  if (!goa_provider_get_all_finish (&providers, res, NULL))
+    goto out;
+
   for (l = providers; l != NULL; l = l->next)
     {
       GoaProvider *p;
@@ -762,7 +771,7 @@ on_manager_handle_add_account (GoaManager             *manager,
 
       p = GOA_PROVIDER (l->data);
       type = goa_provider_get_provider_type (p);
-      if (g_strcmp0 (type, provider_type) == 0)
+      if (g_strcmp0 (type, data->provider_type) == 0)
         {
           provider = p;
           break;
@@ -776,8 +785,8 @@ on_manager_handle_add_account (GoaManager             *manager,
                    GOA_ERROR,
                    GOA_ERROR_FAILED, /* TODO: more specific */
                    _("Failed to find a provider for: %s"),
-                   provider_type);
-      g_dbus_method_invocation_return_gerror (invocation, error);
+                   data->provider_type);
+      g_dbus_method_invocation_return_gerror (data->invocation, error);
       goto out;
     }
 
@@ -785,7 +794,7 @@ on_manager_handle_add_account (GoaManager             *manager,
   path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ());
   error = NULL;
   if (!g_file_get_contents (path,
-                            &data,
+                            &key_file_data,
                             &length,
                             &error))
     {
@@ -796,7 +805,7 @@ on_manager_handle_add_account (GoaManager             *manager,
       else
         {
           g_prefix_error (&error, "Error loading file %s: ", path);
-          g_dbus_method_invocation_return_gerror (invocation, error);
+          g_dbus_method_invocation_return_gerror (data->invocation, error);
           goto out;
         }
     }
@@ -805,22 +814,22 @@ on_manager_handle_add_account (GoaManager             *manager,
       if (length > 0)
         {
           error = NULL;
-          if (!g_key_file_load_from_data (key_file, data, length, G_KEY_FILE_KEEP_COMMENTS, &error))
+          if (!g_key_file_load_from_data (key_file, key_file_data, length, G_KEY_FILE_KEEP_COMMENTS, &error))
             {
               g_prefix_error (&error, "Error parsing key-value-file %s: ", path);
-              g_dbus_method_invocation_return_gerror (invocation, error);
+              g_dbus_method_invocation_return_gerror (data->invocation, error);
               goto out;
             }
         }
     }
 
-  id = generate_new_id (daemon);
+  id = generate_new_id (data->daemon);
   group = g_strdup_printf ("Account %s", id);
-  g_key_file_set_string (key_file, group, "Provider", provider_type);
-  g_key_file_set_string (key_file, group, "Identity", identity);
-  g_key_file_set_string (key_file, group, "PresentationIdentity", presentation_identity);
+  g_key_file_set_string (key_file, group, "Provider", data->provider_type);
+  g_key_file_set_string (key_file, group, "Identity", data->identity);
+  g_key_file_set_string (key_file, group, "PresentationIdentity", data->presentation_identity);
 
-  g_variant_iter_init (&iter, details);
+  g_variant_iter_init (&iter, data->details);
   while (g_variant_iter_next (&iter, "{&s&s}", &key, &value))
     {
       /* We treat IsTemporary special.  If it's true we add in
@@ -833,7 +842,7 @@ on_manager_handle_add_account (GoaManager             *manager,
             {
               const char *guid;
 
-              guid = g_dbus_connection_get_guid (daemon->connection);
+              guid = g_dbus_connection_get_guid (data->daemon->connection);
               g_key_file_set_string (key_file, group, "SessionId", guid);
             }
         }
@@ -841,26 +850,26 @@ on_manager_handle_add_account (GoaManager             *manager,
       g_key_file_set_string (key_file, group, key, value);
     }
 
-  g_free (data);
+  g_free (key_file_data);
   error = NULL;
-  data = g_key_file_to_data (key_file,
-                             &length,
-                             &error);
-  if (data == NULL)
+  key_file_data = g_key_file_to_data (key_file,
+                                      &length,
+                                      &error);
+  if (key_file_data == NULL)
     {
       g_prefix_error (&error, "Error generating key-value-file: ");
-      g_dbus_method_invocation_return_gerror (invocation, error);
+      g_dbus_method_invocation_return_gerror (data->invocation, error);
       goto out;
     }
 
   error = NULL;
   if (!g_file_set_contents (path,
-                            data,
+                            key_file_data,
                             length,
                             &error))
     {
       g_prefix_error (&error, "Error writing key-value-file %s: ", path);
-      g_dbus_method_invocation_return_gerror (invocation, error);
+      g_dbus_method_invocation_return_gerror (data->invocation, error);
       goto out;
     }
 
@@ -869,26 +878,62 @@ on_manager_handle_add_account (GoaManager             *manager,
    */
   goa_utils_store_credentials_for_id_sync (provider,
                                            id,
-                                           credentials,
+                                           data->credentials,
                                            NULL, /* GCancellable */
                                            NULL);
 
-  goa_daemon_reload_configuration (daemon);
+  goa_daemon_reload_configuration (data->daemon);
 
   object_path = g_strdup_printf ("/org/gnome/OnlineAccounts/Accounts/%s", id);
-  goa_manager_complete_add_account (manager, invocation, object_path);
+  goa_manager_complete_add_account (data->manager, data->invocation, object_path);
 
  out:
   g_free (object_path);
   if (providers != NULL)
     g_list_free_full (providers, g_object_unref);
-  g_free (data);
+  g_free (key_file_data);
   g_free (group);
   g_free (id);
   g_free (path);
   if (key_file != NULL)
     g_key_file_free (key_file);
 
+  g_object_unref (data->daemon);
+  g_object_unref (data->manager);
+  g_object_unref (data->invocation);
+  g_free (data->provider_type);
+  g_free (data->identity);
+  g_free (data->presentation_identity);
+  g_variant_unref (data->credentials);
+  g_variant_unref (data->details);
+  g_slice_free (AddAccountData, data);
+}
+
+static gboolean
+on_manager_handle_add_account (GoaManager             *manager,
+                               GDBusMethodInvocation  *invocation,
+                               const gchar            *provider_type,
+                               const gchar            *identity,
+                               const gchar            *presentation_identity,
+                               GVariant               *credentials,
+                               GVariant               *details,
+                               gpointer                user_data)
+{
+  GoaDaemon *daemon = GOA_DAEMON (user_data);
+  AddAccountData *data;
+
+  data = g_slice_new0 (AddAccountData);
+  data->daemon = g_object_ref (daemon);
+  data->manager = g_object_ref (manager);
+  data->invocation = g_object_ref (invocation);
+  data->provider_type = g_strdup (provider_type);
+  data->identity = g_strdup (identity);
+  data->presentation_identity = g_strdup (presentation_identity);
+  data->credentials = g_variant_ref (credentials);
+  data->details = g_variant_ref (details);
+
+  goa_provider_get_all (get_all_providers_cb, data);
+
   return TRUE; /* invocation was handled */
 }
 
diff --git a/src/examples/list-providers.c b/src/examples/list-providers.c
index 3aa93ad..3079f1f 100644
--- a/src/examples/list-providers.c
+++ b/src/examples/list-providers.c
@@ -25,14 +25,45 @@
 #define GOA_BACKEND_API_IS_SUBJECT_TO_CHANGE
 #include <goabackend/goabackend.h>
 
+typedef struct
+{
+  GMainLoop *loop;
+  GList *providers;
+  GError *error;
+} GetAllData;
+
+static void
+get_all_cb (GObject      *source,
+            GAsyncResult *res,
+            gpointer      user_data)
+{
+  GetAllData *data = user_data;
+
+  goa_provider_get_all_finish (&data->providers, res, &data->error);
+  g_main_loop_quit (data->loop);
+}
+
 int
 main (int argc, char **argv)
 {
+  GetAllData data = {0,};
   GoaProvider *provider;
-  GList *providers, *l;
+  GList *l;
+
+  data.loop = g_main_loop_new (NULL, FALSE);
+  goa_provider_get_all (get_all_cb, &data);
+  g_main_loop_run (data.loop);
 
-  providers = goa_provider_get_all ();
-  for (l = providers; l != NULL; l = l->next) {
+  if (data.error != NULL) {
+    g_printerr ("Failed to list providers: %s (%s, %d)\n",
+        data.error->message,
+        g_quark_to_string (data.error->domain),
+        data.error->code);
+    g_error_free (data.error);
+    goto out;
+  }
+
+  for (l = data.providers; l != NULL; l = l->next) {
     char *provider_name;
 
     provider = GOA_PROVIDER (l->data);
@@ -42,5 +73,9 @@ main (int argc, char **argv)
     provider = NULL;
   }
 
+out:
+  g_main_loop_unref (data.loop);
+  g_list_free_full (data.providers, g_object_unref);
+
   return 0;
 }
diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c
index 9880c2c..7d7170c 100644
--- a/src/goabackend/goaprovider.c
+++ b/src/goabackend/goaprovider.c
@@ -899,37 +899,193 @@ goa_provider_get_for_provider_type (const gchar *provider_type)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+typedef struct
+{
+  GQueue ret;
+  gint pending_calls;
+  GSimpleAsyncResult *result;
+} GetAllData;
+
+static void
+free_list_and_unref (gpointer data)
+{
+  g_list_free_full (data, g_object_unref);
+}
+
+static gint
+compare_providers (GoaProvider *a,
+                   GoaProvider *b)
+{
+  gboolean a_branded;
+  gboolean b_branded;
+
+  if (goa_provider_get_provider_features (a) & GOA_PROVIDER_FEATURE_BRANDED)
+    a_branded = TRUE;
+  else
+    a_branded = FALSE;
+
+  if (goa_provider_get_provider_features (b) & GOA_PROVIDER_FEATURE_BRANDED)
+    b_branded = TRUE;
+  else
+    b_branded = FALSE;
+
+  /* g_queue_sort() uses a stable sort, so, if we return 0, the order
+   * is not changed. */
+  if (a_branded == b_branded)
+    return 0;
+  else if (a_branded)
+    return -1;
+  else
+    return 1;
+}
+
+static void
+get_all_check_done (GetAllData *data)
+{
+  if (data->pending_calls > 0)
+    return;
+
+  /* Make sure that branded providers come first, but don't change the
+   * order otherwise. */
+  g_queue_sort (&data->ret, (GCompareDataFunc) compare_providers, NULL);
+
+  /* Steal the list out of the GQueue. */
+  g_simple_async_result_set_op_res_gpointer (data->result, data->ret.head,
+      free_list_and_unref);
+  g_simple_async_result_complete_in_idle (data->result);
+
+  g_object_unref (data->result);
+  g_slice_free (GetAllData, data);
+}
+
+static void
+get_providers_cb (GObject      *source,
+                  GAsyncResult *res,
+                  gpointer      user_data)
+{
+  GoaProviderFactory *factory = GOA_PROVIDER_FACTORY (source);
+  GetAllData *data = user_data;
+  GList *providers = NULL;
+  GList *l;
+  GError *error = NULL;
+
+  if (!goa_provider_factory_get_providers_finish (factory, &providers, res, &error))
+    {
+      goa_error ("Error getting providers from a factory: %s (%s, %d)",
+          error->message,
+          g_quark_to_string (error->domain),
+          error->code);
+      g_clear_error (&error);
+      goto out;
+    }
+
+  for (l = providers; l != NULL; l = l->next)
+    {
+      /* Steal the value */
+      g_queue_push_tail (&data->ret, l->data);
+    }
+
+  g_list_free (providers);
+
+out:
+  data->pending_calls--;
+  get_all_check_done (data);
+}
+
 /**
  * goa_provider_get_all:
+ * @callback: The function to call when the request is satisfied.
+ * @user_data: Pointer to pass to @callback.
  *
- * Looks up the %GOA_PROVIDER_EXTENSION_POINT_NAME extension
- * point and returns a newly created #GoaProvider for each
- * provider type encountered.
+ * Creates a list of all the available #GoaProvider instances.
+ *
+ * When the result is ready, @callback will be called in the the <link
+ * linkend="g-main-context-push-thread-default">thread-default main
+ * loop</link> this function was called from. You can then call
+ * goa_provider_get_all_finish() to get the result of the operation.
+ *
+ * See goa_provider_get_for_provider_type() for details on how the providers
+ * are found.
  *
  * Returns: (transfer full) (element-type GoaProvider): A list
  *   of element providers that should be freed with g_list_free()
  *   after each element has been freed with g_object_unref().
  */
-GList *
-goa_provider_get_all (void)
+void
+goa_provider_get_all (GAsyncReadyCallback callback,
+                      gpointer            user_data)
 {
-  GList *ret;
   GList *extensions;
   GList *l;
   GIOExtensionPoint *extension_point;
+  GetAllData *data;
+  gint i;
 
   ensure_builtins_loaded ();
 
-  ret = NULL;
+  data = g_slice_new0 (GetAllData);
+  data->result = g_simple_async_result_new (NULL, callback, user_data,
+      goa_provider_get_all);
+  g_queue_init (&data->ret);
+
+  /* Load the normal providers. */
   extension_point = g_io_extension_point_lookup (GOA_PROVIDER_EXTENSION_POINT_NAME);
   extensions = g_io_extension_point_get_extensions (extension_point);
   /* TODO: what if there are two extensions with the same name? */
-  for (l = extensions; l != NULL; l = l->next)
+  for (l = extensions, i = 0; l != NULL; l = l->next, i++)
     {
       GIOExtension *extension = l->data;
-      ret = g_list_prepend (ret, g_object_new (g_io_extension_get_type (extension), NULL));
+      /* The extensions are loaded in the reverse order we used in
+       * ensure_builtins_loaded, so we need to push extension if front of
+       * the already loaded ones. */
+      g_queue_push_head (&data->ret, g_object_new (g_io_extension_get_type (extension), NULL));
     }
-  return ret;
+
+  /* Load the provider factories and get the dynamic providers out of them. */
+  extension_point = g_io_extension_point_lookup (GOA_PROVIDER_FACTORY_EXTENSION_POINT_NAME);
+  extensions = g_io_extension_point_get_extensions (extension_point);
+  for (l = extensions, i = 0; l != NULL; l = l->next, i++)
+    {
+      GIOExtension *extension = l->data;
+      goa_provider_factory_get_providers (g_object_new (g_io_extension_get_type (extension), NULL),
+          get_providers_cb, data);
+      data->pending_calls++;
+    }
+
+  get_all_check_done (data);
+}
+
+/**
+ * goa_provider_get_all_finish:
+ * @out_providers: (out): Return location for a list of #GoaProvider instances.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to goa_provider_get_all().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with goa_provider_get_all().
+ *
+ * Returns: %TRUE if the list was successfully retrieved, %FALSE if @error is set.
+ */
+gboolean
+goa_provider_get_all_finish (GList        **out_providers,
+                             GAsyncResult  *result,
+                             GError       **error)
+{
+  GSimpleAsyncResult *simple = (GSimpleAsyncResult *) result;
+  GList *providers;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
+        goa_provider_get_all), FALSE);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  if (out_providers != NULL)
+    {
+      providers = g_simple_async_result_get_op_res_gpointer (simple);
+      *out_providers = g_list_copy_deep (providers, (GCopyFunc) g_object_ref, NULL);
+    }
+
+  return TRUE;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/goabackend/goaprovider.h b/src/goabackend/goaprovider.h
index 89fb826..67aa5fd 100644
--- a/src/goabackend/goaprovider.h
+++ b/src/goabackend/goaprovider.h
@@ -174,7 +174,11 @@ gboolean     goa_provider_ensure_credentials_sync   (GoaProvider         *provid
                                                      GError             **error);
 guint        goa_provider_get_credentials_generation (GoaProvider        *provider);
 
-GList        *goa_provider_get_all (void);
+void          goa_provider_get_all                  (GAsyncReadyCallback  callback,
+                                                     gpointer             user_data);
+gboolean      goa_provider_get_all_finish           (GList              **out_providers,
+                                                     GAsyncResult        *result,
+                                                     GError             **error);
 
 GoaProvider  *goa_provider_get_for_provider_type (const gchar *provider_type);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]