[gnome-software: 42/72] packagekit-refine-repos: Make refine asynchronous




commit 68e083ff39c9ac7a59ce7b46e4ec46887e071e67
Author: Philip Withnall <pwithnall endlessos org>
Date:   Fri Nov 5 17:27:55 2021 +0000

    packagekit-refine-repos: Make refine asynchronous
    
    The handling of `gs_packagekit_helper_cb()` and its data will be
    reworked in a subsequent commit. It currently (temporarily,
    deliberately) leaks the helper struct.
    
    Signed-off-by: Philip Withnall <pwithnall endlessos org>
    
    Helps: #1472

 .../packagekit/gs-plugin-packagekit-refine-repos.c | 200 +++++++++++++++++----
 1 file changed, 170 insertions(+), 30 deletions(-)
---
diff --git a/plugins/packagekit/gs-plugin-packagekit-refine-repos.c 
b/plugins/packagekit/gs-plugin-packagekit-refine-repos.c
index e1bc35b9f..5dd5032a4 100644
--- a/plugins/packagekit/gs-plugin-packagekit-refine-repos.c
+++ b/plugins/packagekit/gs-plugin-packagekit-refine-repos.c
@@ -69,22 +69,127 @@ gs_plugin_packagekit_refine_repos_finalize (GObject *object)
        G_OBJECT_CLASS (gs_plugin_packagekit_refine_repos_parent_class)->finalize (object);
 }
 
-gboolean
-gs_plugin_refine (GsPlugin *plugin,
-                  GsAppList *list,
-                  GsPluginRefineFlags flags,
-                  GCancellable *cancellable,
-                  GError **error)
+typedef struct {
+       /* Track pending operations. */
+       guint n_pending_operations;
+       gboolean completed;
+       GError *error;  /* (nullable) (owned) */
+} RefineData;
+
+static void
+refine_data_free (RefineData *data)
+{
+       g_assert (data->n_pending_operations == 0);
+       g_assert (data->completed);
+
+       g_clear_error (&data->error);
+       g_free (data);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (RefineData, refine_data_free)
+
+static GTask *
+refine_task_add_operation (GTask *refine_task)
+{
+       RefineData *data = g_task_get_task_data (refine_task);
+
+       g_assert (!data->completed);
+       data->n_pending_operations++;
+
+       return g_object_ref (refine_task);
+}
+
+static void
+refine_task_complete_operation (GTask *refine_task)
+{
+       RefineData *data = g_task_get_task_data (refine_task);
+
+       g_assert (data->n_pending_operations > 0);
+       data->n_pending_operations--;
+
+       /* Have all operations completed? */
+       if (data->n_pending_operations == 0) {
+               g_assert (!data->completed);
+               data->completed = TRUE;
+
+               if (data->error != NULL)
+                       g_task_return_error (refine_task, g_steal_pointer (&data->error));
+               else
+                       g_task_return_boolean (refine_task, TRUE);
+       }
+}
+
+static void
+refine_task_complete_operation_with_error (GTask  *refine_task,
+                                          GError *error  /* (transfer full) */)
+{
+       RefineData *data = g_task_get_task_data (refine_task);
+       g_autoptr(GError) owned_error = g_steal_pointer (&error);
+
+       /* Multiple operations might fail. Just take the first error. */
+       if (data->error == NULL)
+               data->error = g_steal_pointer (&owned_error);
+
+       refine_task_complete_operation (refine_task);
+}
+
+typedef struct {
+       GTask *refine_task;  /* (owned) (not nullable) */
+       GsApp *app;  /* (owned) (not nullable) */
+       gchar *filename;  /* (owned) (not nullable) */
+} SearchFilesData;
+
+static void
+search_files_data_free (SearchFilesData *data)
+{
+       g_free (data->filename);
+       g_clear_object (&data->app);
+       g_clear_object (&data->refine_task);
+       g_free (data);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SearchFilesData, search_files_data_free)
+
+static SearchFilesData *
+search_files_data_new_operation (GTask       *refine_task,
+                                 GsApp       *app,
+                                 const gchar *filename)
+{
+       g_autoptr(SearchFilesData) data = g_new0 (SearchFilesData, 1);
+       data->refine_task = refine_task_add_operation (refine_task);
+       data->app = g_object_ref (app);
+       data->filename = g_strdup (filename);
+
+       return g_steal_pointer (&data);
+}
+
+static void search_files_cb (GObject      *source_object,
+                             GAsyncResult *result,
+                             gpointer      user_data);
+
+static void
+gs_plugin_packagekit_refine_repos_refine_async (GsPlugin            *plugin,
+                                                GsAppList           *list,
+                                                GsPluginRefineFlags  flags,
+                                                GCancellable        *cancellable,
+                                                GAsyncReadyCallback  callback,
+                                                gpointer             user_data)
 {
        GsPluginPackagekitRefineRepos *self = GS_PLUGIN_PACKAGEKIT_REFINE_REPOS (plugin);
+       g_autoptr(GTask) task = NULL;
+       g_autoptr(RefineData) data = NULL;
+
+       task = g_task_new (plugin, cancellable, callback, user_data);
+       g_task_set_source_tag (task, gs_plugin_packagekit_refine_repos_refine_async);
+       data = g_new0 (RefineData, 1);
+       data->n_pending_operations = 1;  /* to prevent the task being completed before all operations have 
been started */
+       g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) refine_data_free);
 
        for (guint i = 0; i < gs_app_list_length (list); i++) {
                GsApp *app = gs_app_list_index (list, i);
                const gchar *filename;
                const gchar *to_array[] = { NULL, NULL };
                g_autoptr(GsPackagekitHelper) helper = gs_packagekit_helper_new (plugin);
-               g_autoptr(PkResults) results = NULL;
-               g_autoptr(GPtrArray) packages = NULL;
 
                if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD))
                        continue;
@@ -101,40 +206,75 @@ gs_plugin_refine (GsPlugin *plugin,
                gs_packagekit_helper_add_app (helper, app);
                g_mutex_lock (&self->client_mutex);
                pk_client_set_interactive (self->client, gs_plugin_has_flags (plugin, 
GS_PLUGIN_FLAGS_INTERACTIVE));
-               results = pk_client_search_files (self->client,
-                                                 pk_bitfield_from_enums (PK_FILTER_ENUM_INSTALLED, -1),
-                                                 (gchar **) to_array,
-                                                 cancellable,
-                                                 gs_packagekit_helper_cb, helper,
-                                                 error);
+               pk_client_search_files_async (self->client,
+                                             pk_bitfield_from_enums (PK_FILTER_ENUM_INSTALLED, -1),
+                                             (gchar **) to_array,
+                                             cancellable,
+                                             /* TODO: @helper is leaked here; this will be reworked in a 
subsequent commit */
+                                             gs_packagekit_helper_cb, g_object_ref (helper),
+                                             search_files_cb,
+                                             search_files_data_new_operation (task, app, filename));
                g_mutex_unlock (&self->client_mutex);
-               if (!gs_plugin_packagekit_results_valid (results, error)) {
-                       g_prefix_error (error, "failed to search file %s: ", filename);
-                       return FALSE;
-               }
-
-               /* get results */
-               packages = pk_results_get_package_array (results);
-               if (packages->len == 1) {
-                       PkPackage *package = g_ptr_array_index (packages, 0);
-                       gs_app_add_source_id (app, pk_package_get_id (package));
-               } else {
-                       g_debug ("failed to find one package for repo %s, %s, [%u]",
-                                gs_app_get_id (app), filename, packages->len);
-               }
        }
 
-       /* success */
-       return TRUE;
+       /* Mark the operation to set up all the other operations as completed.
+        * The @refine_task will now be completed once all the async operations
+        * have completed, and the task callback invoked. */
+       refine_task_complete_operation (task);
+}
+
+static void
+search_files_cb (GObject      *source_object,
+                 GAsyncResult *result,
+                 gpointer      user_data)
+{
+       PkClient *client = PK_CLIENT (source_object);
+       g_autoptr(SearchFilesData) search_files_data = g_steal_pointer (&user_data);
+       GTask *refine_task = search_files_data->refine_task;
+       g_autoptr(PkResults) results = NULL;
+       g_autoptr(GPtrArray) packages = NULL;
+       g_autoptr(GError) local_error = NULL;
+
+       results = pk_client_generic_finish (client, result, &local_error);
+
+       if (!gs_plugin_packagekit_results_valid (results, &local_error)) {
+               g_prefix_error (&local_error, "failed to search file %s: ", search_files_data->filename);
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       /* get results */
+       packages = pk_results_get_package_array (results);
+       if (packages->len == 1) {
+               PkPackage *package = g_ptr_array_index (packages, 0);
+               gs_app_add_source_id (search_files_data->app, pk_package_get_id (package));
+       } else {
+               g_debug ("failed to find one package for repo %s, %s, [%u]",
+                        gs_app_get_id (search_files_data->app), search_files_data->filename, packages->len);
+       }
+
+       refine_task_complete_operation (refine_task);
+}
+
+static gboolean
+gs_plugin_packagekit_refine_repos_refine_finish (GsPlugin      *plugin,
+                                                 GAsyncResult  *result,
+                                                 GError       **error)
+{
+       return g_task_propagate_boolean (G_TASK (result), error);
 }
 
 static void
 gs_plugin_packagekit_refine_repos_class_init (GsPluginPackagekitRefineReposClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GsPluginClass *plugin_class = GS_PLUGIN_CLASS (klass);
 
        object_class->dispose = gs_plugin_packagekit_refine_repos_dispose;
        object_class->finalize = gs_plugin_packagekit_refine_repos_finalize;
+
+       plugin_class->refine_async = gs_plugin_packagekit_refine_repos_refine_async;
+       plugin_class->refine_finish = gs_plugin_packagekit_refine_repos_refine_finish;
 }
 
 GType


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