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




commit 54d5c7f068a1011f448a1634795b7452862faab2
Author: Philip Withnall <pwithnall endlessos org>
Date:   Tue Oct 26 23:30:19 2021 +0100

    packagekit: Make refine asynchronous
    
    This is a bit more complex than other refine functions, as there are
    multiple operations which can proceed in parallel as part of the refine.
    
    Return results only when they have all completed.
    
    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

 plugins/packagekit/gs-plugin-packagekit.c | 793 +++++++++++++++++++++---------
 1 file changed, 547 insertions(+), 246 deletions(-)
---
diff --git a/plugins/packagekit/gs-plugin-packagekit.c b/plugins/packagekit/gs-plugin-packagekit.c
index 366bbec57..9339d4a57 100644
--- a/plugins/packagekit/gs-plugin-packagekit.c
+++ b/plugins/packagekit/gs-plugin-packagekit.c
@@ -92,18 +92,6 @@ static gboolean reload_proxy_settings_finish (GsPluginPackagekit  *self,
                                               GAsyncResult        *result,
                                               GError             **error);
 
-static void
-async_result_cb (GObject      *source_object,
-                 GAsyncResult *result,
-                 gpointer      user_data)
-{
-       /* FIXME: This is not the right way to write an async-to-sync converter,
-        * but this is temporary and will be removed in a subsequent commit. */
-       GAsyncResult **result_out = user_data;
-       *result_out = g_object_ref (result);
-       g_main_context_wakeup (NULL);
-}
-
 static void
 gs_plugin_packagekit_init (GsPluginPackagekit *self)
 {
@@ -1140,18 +1128,158 @@ gs_plugin_packagekit_refine_valid_package_name (const gchar *source)
        return TRUE;
 }
 
-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) */
+
+       /* Input data for operations. */
+       GsAppList *full_list;  /* (nullable) (owned) */
+       GsAppList *resolve_list;  /* (nullable) (owned) */
+       GsApp *app_operating_system;  /* (nullable) (owned) */
+       GsAppList *update_details_list;  /* (nullable) (owned) */
+       GsAppList *details_list;  /* (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_clear_object (&data->full_list);
+       g_clear_object (&data->resolve_list);
+       g_clear_object (&data->app_operating_system);
+       g_clear_object (&data->update_details_list);
+       g_clear_object (&data->details_list);
+
+       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 upgrade_system_cb (GObject      *source_object,
+                               GAsyncResult *result,
+                               gpointer      user_data);
+static void resolve_all_packages_with_filter_cb (GObject      *source_object,
+                                                 GAsyncResult *result,
+                                                 gpointer      user_data);
+static void search_files_cb (GObject      *source_object,
+                             GAsyncResult *result,
+                             gpointer      user_data);
+static void get_update_detail_cb (GObject      *source_object,
+                                  GAsyncResult *result,
+                                  gpointer      user_data);
+static void get_details_cb (GObject      *source_object,
+                            GAsyncResult *result,
+                            gpointer      user_data);
+static void get_updates_cb (GObject      *source_object,
+                            GAsyncResult *result,
+                            gpointer      user_data);
+static void refine_all_history_cb (GObject      *source_object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data);
+
+static void
+gs_plugin_packagekit_refine_async (GsPlugin            *plugin,
+                                   GsAppList           *list,
+                                   GsPluginRefineFlags  flags,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data)
 {
        GsPluginPackagekit *self = GS_PLUGIN_PACKAGEKIT (plugin);
        g_autoptr(GsAppList) resolve_list = gs_app_list_new ();
        g_autoptr(GsAppList) update_details_list = gs_app_list_new ();
        g_autoptr(GsAppList) details_list = gs_app_list_new ();
        g_autoptr(GsAppList) history_list = gs_app_list_new ();
+       g_autoptr(GTask) task = NULL;
+       g_autoptr(RefineData) data = NULL;
+       RefineData *data_unowned = NULL;
+
+       task = g_task_new (plugin, cancellable, callback, user_data);
+       g_task_set_source_tag (task, gs_plugin_packagekit_refine_async);
+       data_unowned = data = g_new0 (RefineData, 1);
+       data->full_list = g_object_ref (list);
+       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);
 
        /* Process the @list and work out what information is needed for each
         * app. */
@@ -1202,10 +1330,7 @@ gs_plugin_refine (GsPlugin *plugin,
        if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPGRADE_REMOVED) {
                for (guint i = 0; i < gs_app_list_length (list); i++) {
                        GsApp *app = gs_app_list_index (list, i);
-                       GsApp *app2;
                        g_autoptr(GsPackagekitHelper) helper = gs_packagekit_helper_new (plugin);
-                       g_autoptr(PkResults) results = NULL;
-                       g_autoptr(GsAppList) results_list = NULL;
                        guint cache_age_save;
 
                        if (gs_app_get_kind (app) != AS_COMPONENT_KIND_OPERATING_SYSTEM)
@@ -1213,93 +1338,54 @@ gs_plugin_refine (GsPlugin *plugin,
 
                        gs_packagekit_helper_add_app (helper, app);
 
+                       /* Expose the @app to the callback functions so that
+                        * upgrade packages can be added as related. This only
+                        * supports one OS. */
+                       g_assert (data_unowned->app_operating_system == NULL);
+                       data_unowned->app_operating_system = g_object_ref (app);
+
                        /* ask PK to simulate upgrading the system */
                        g_mutex_lock (&self->client_mutex_refine);
                        cache_age_save = pk_client_get_cache_age (self->client_refine);
                        pk_client_set_cache_age (self->client_refine, 60 * 60 * 24 * 7); /* once per week */
                        pk_client_set_interactive (self->client_refine, gs_plugin_has_flags (plugin, 
GS_PLUGIN_FLAGS_INTERACTIVE));
-                       results = pk_client_upgrade_system (self->client_refine,
-                                                           pk_bitfield_from_enums 
(PK_TRANSACTION_FLAG_ENUM_SIMULATE, -1),
-                                                           gs_app_get_version (app),
-                                                           PK_UPGRADE_KIND_ENUM_COMPLETE,
-                                                           cancellable,
-                                                           gs_packagekit_helper_cb, helper,
-                                                           error);
+                       pk_client_upgrade_system_async (self->client_refine,
+                                                       pk_bitfield_from_enums 
(PK_TRANSACTION_FLAG_ENUM_SIMULATE, -1),
+                                                       gs_app_get_version (app),
+                                                       PK_UPGRADE_KIND_ENUM_COMPLETE,
+                                                       cancellable,
+                                                       /* TODO: @helper is leaked here; this will be 
reworked in a subsequent commit */
+                                                       gs_packagekit_helper_cb, g_object_ref (helper),
+                                                       upgrade_system_cb,
+                                                       refine_task_add_operation (task));
                        pk_client_set_cache_age (self->client_refine, cache_age_save);
                        g_mutex_unlock (&self->client_mutex_refine);
 
-                       if (!gs_plugin_packagekit_results_valid (results, error)) {
-                               g_prefix_error (error, "failed to refine distro upgrade: ");
-                               return FALSE;
-                       }
-                       results_list = gs_app_list_new ();
-                       if (!gs_plugin_packagekit_add_results (plugin, results_list, results, error))
-                               return FALSE;
-
-                       /* add each of these as related applications */
-                       for (guint j = 0; j < gs_app_list_length (results_list); j++) {
-                               app2 = gs_app_list_index (results_list, j);
-                               if (gs_app_get_state (app2) != GS_APP_STATE_UNAVAILABLE)
-                                       continue;
-                               gs_app_add_related (app, app2);
-                       }
+                       /* Only support one operating system. */
+                       break;
                }
        }
 
        /* can we resolve in one go? */
        if (gs_app_list_length (resolve_list) > 0) {
                PkBitfield filter;
-               g_autoptr(GsAppList) resolve2_list = NULL;
-               g_autoptr(GAsyncResult) async_result = NULL;
-               g_autoptr(GAsyncResult) async_result2 = NULL;
+
+               /* Expose the @resolve_list to the callback functions in case a
+                * second attempt is needed. */
+               g_assert (data_unowned->resolve_list == NULL);
+               data_unowned->resolve_list = g_object_ref (resolve_list);
 
                /* first, try to resolve packages with ARCH filter */
                filter = pk_bitfield_from_enums (PK_FILTER_ENUM_NEWEST,
                                                 PK_FILTER_ENUM_ARCH,
                                                 -1);
 
-               /* FIXME: This async-to-sync conversion is very hacky, but is
-                * temporary and will be removed in a subsequent commit. */
                gs_plugin_packagekit_resolve_packages_with_filter_async (self,
                                                                         resolve_list,
                                                                         filter,
                                                                         cancellable,
-                                                                        async_result_cb,
-                                                                        &async_result);
-               while (async_result == NULL)
-                       g_main_context_iteration (NULL, TRUE);
-               if (!gs_plugin_packagekit_resolve_packages_with_filter_finish (self,
-                                                                              async_result,
-                                                                              error))
-                       return FALSE;
-
-               /* if any packages remaining in UNKNOWN state, try to resolve them again,
-                * but this time without ARCH filter */
-               resolve2_list = gs_app_list_new ();
-               for (guint i = 0; i < gs_app_list_length (resolve_list); i++) {
-                       GsApp *app = gs_app_list_index (resolve_list, i);
-                       if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN)
-                               gs_app_list_add (resolve2_list, app);
-               }
-               filter = pk_bitfield_from_enums (PK_FILTER_ENUM_NEWEST,
-                                                PK_FILTER_ENUM_NOT_ARCH,
-                                                PK_FILTER_ENUM_NOT_SOURCE,
-                                                -1);
-               g_clear_object (&async_result);
-
-               /* FIXME: This async-to-sync conversion is also hacky but temporary. */
-               gs_plugin_packagekit_resolve_packages_with_filter_async (self,
-                                                                        resolve2_list,
-                                                                        filter,
-                                                                        cancellable,
-                                                                        async_result_cb,
-                                                                        &async_result);
-               while (async_result == NULL)
-                       g_main_context_iteration (NULL, TRUE);
-               if (!gs_plugin_packagekit_resolve_packages_with_filter_finish (self,
-                                                                              async_result,
-                                                                              error))
-                       return FALSE;
+                                                                        resolve_all_packages_with_filter_cb,
+                                                                        refine_task_add_operation (task));
        }
 
        /* set the package-id for an installed desktop file */
@@ -1310,8 +1396,6 @@ gs_plugin_refine (GsPlugin *plugin,
                        const gchar *tmp;
                        const gchar *to_array[] = { NULL, NULL };
                        g_autoptr(GsPackagekitHelper) helper = NULL;
-                       g_autoptr(PkResults) results = NULL;
-                       g_autoptr(GPtrArray) packages = NULL;
 
                        if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD))
                                continue;
@@ -1349,41 +1433,28 @@ gs_plugin_refine (GsPlugin *plugin,
                        gs_packagekit_helper_add_app (helper, app);
                        g_mutex_lock (&self->client_mutex_refine);
                        pk_client_set_interactive (self->client_refine, gs_plugin_has_flags (plugin, 
GS_PLUGIN_FLAGS_INTERACTIVE));
-                       results = pk_client_search_files (self->client_refine,
-                                                         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_refine,
+                                                     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, fn));
                        g_mutex_unlock (&self->client_mutex_refine);
-                       if (!gs_plugin_packagekit_results_valid (results, error)) {
-                               g_prefix_error (error, "failed to search file %s: ", fn);
-                               return FALSE;
-                       }
-
-                       /* get results */
-                       packages = pk_results_get_package_array (results);
-                       if (packages->len == 1) {
-                               PkPackage *package;
-                               package = g_ptr_array_index (packages, 0);
-                               gs_plugin_packagekit_set_metadata_from_package (plugin, app, package);
-                       } else {
-                               g_warning ("Failed to find one package for %s, %s, [%u]",
-                                          gs_app_get_id (app), fn, packages->len);
-                       }
                }
        }
 
        /* any update details missing? */
        if (gs_app_list_length (update_details_list) > 0) {
-               const gchar *package_id;
-               guint j;
                GsApp *app;
-               PkUpdateDetail *update_detail;
                g_autoptr(GsPackagekitHelper) helper = gs_packagekit_helper_new (plugin);
                g_autofree const gchar **package_ids = NULL;
-               g_autoptr(PkResults) results = NULL;
-               g_autoptr(GPtrArray) array = NULL;
+
+               /* Expose the @update_details_list to the callback functions so
+                * its apps can be updated. */
+               g_assert (data_unowned->update_details_list == NULL);
+               data_unowned->update_details_list = g_object_ref (update_details_list);
 
                package_ids = g_new0 (const gchar *, gs_app_list_length (update_details_list) + 1);
                for (guint i = 0; i < gs_app_list_length (update_details_list); i++) {
@@ -1395,37 +1466,14 @@ gs_plugin_refine (GsPlugin *plugin,
                /* get any update details */
                g_mutex_lock (&self->client_mutex_refine);
                pk_client_set_interactive (self->client_refine, gs_plugin_has_flags (plugin, 
GS_PLUGIN_FLAGS_INTERACTIVE));
-               results = pk_client_get_update_detail (self->client_refine,
-                                                      (gchar **) package_ids,
-                                                      cancellable,
-                                                      gs_packagekit_helper_cb, helper,
-                                                      error);
+               pk_client_get_update_detail_async (self->client_refine,
+                                                  (gchar **) package_ids,
+                                                  cancellable,
+                                                  /* TODO: @helper is leaked here; this will be reworked in 
a subsequent commit */
+                                                  gs_packagekit_helper_cb, g_object_ref (helper),
+                                                  get_update_detail_cb,
+                                                  refine_task_add_operation (task));
                g_mutex_unlock (&self->client_mutex_refine);
-               if (!gs_plugin_packagekit_results_valid (results, error)) {
-                       g_prefix_error (error, "failed to get update details for %s: ",
-                                       package_ids[0]);
-                       return FALSE;
-               }
-
-               /* set the update details for the update */
-               array = pk_results_get_update_detail_array (results);
-               for (j = 0; j < gs_app_list_length (update_details_list); j++) {
-                       app = gs_app_list_index (update_details_list, j);
-                       package_id = gs_app_get_source_id_default (app);
-                       for (guint i = 0; i < array->len; i++) {
-                               const gchar *tmp;
-                               g_autofree gchar *desc = NULL;
-                               /* right package? */
-                               update_detail = g_ptr_array_index (array, i);
-                               if (g_strcmp0 (package_id, pk_update_detail_get_package_id (update_detail)) 
!= 0)
-                                       continue;
-                               tmp = pk_update_detail_get_update_text (update_detail);
-                               desc = gs_plugin_packagekit_fixup_update_description (tmp);
-                               if (desc != NULL)
-                                       gs_app_set_update_details_markup (app, desc);
-                               break;
-                       }
-               }
        }
 
        /* any package details missing? */
@@ -1433,120 +1481,45 @@ gs_plugin_refine (GsPlugin *plugin,
                g_autoptr(GsPackagekitHelper) helper = gs_packagekit_helper_new (plugin);
                g_autoptr(GPtrArray) package_ids = NULL;
 
+               /* Expose the @details_list to the callback functions so
+                * its apps can be updated. */
+               g_assert (data_unowned->details_list == NULL);
+               data_unowned->details_list = g_object_ref (details_list);
+
                package_ids = app_list_get_package_ids (details_list, NULL, FALSE);
 
                if (package_ids->len > 0) {
                        /* get any details */
                        g_mutex_lock (&self->client_mutex_refine);
                        pk_client_set_interactive (self->client_refine, gs_plugin_has_flags (plugin, 
GS_PLUGIN_FLAGS_INTERACTIVE));
-                       results = pk_client_get_details (self->client_refine,
-                                                        (gchar **) package_ids->pdata,
-                                                        cancellable,
-                                                        gs_packagekit_helper_cb, helper,
-                                                        error);
+                       pk_client_get_details_async (self->client_refine,
+                                                    (gchar **) package_ids->pdata,
+                                                    cancellable,
+                                                    /* TODO: @helper is leaked here; this will be reworked 
in a subsequent commit */
+                                                    gs_packagekit_helper_cb, g_object_ref (helper),
+                                                    get_details_cb,
+                                                    refine_task_add_operation (task));
                        g_mutex_unlock (&self->client_mutex_refine);
-                       if (!gs_plugin_packagekit_results_valid (results, error)) {
-                               g_autofree gchar *package_ids_str = g_strjoinv (",", (gchar **) 
package_ids->pdata);
-                               g_prefix_error (error, "failed to get details for %s: ",
-                                               package_ids_str);
-                               return FALSE;
-                       }
-
-                       /* get the results and copy them into a hash table for fast lookups:
-                        * there are typically 400 to 700 elements in @array, and 100 to 200
-                        * elements in @list, each with 1 or 2 source IDs to look up (but
-                        * sometimes 200) */
-                       array = pk_results_get_details_array (results);
-                       details_collection = gs_plugin_packagekit_details_array_to_hash (array);
-
-                       /* set the update details for the update */
-                       for (i = 0; i < gs_app_list_length (details_list); i++) {
-                               app = gs_app_list_index (details_list, i);
-                               gs_plugin_packagekit_refine_details_app (plugin, details_collection, app);
-                       }
                }
        }
 
        /* get the update severity */
        if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_SEVERITY) != 0) {
-               GsApp *app;
-               const gchar *package_id;
                PkBitfield filter;
                g_autoptr(GsPackagekitHelper) helper = gs_packagekit_helper_new (plugin);
-               g_autoptr(PkPackageSack) sack = NULL;
-               g_autoptr(PkResults) results = NULL;
 
                /* get the list of updates */
                filter = pk_bitfield_value (PK_FILTER_ENUM_NONE);
                g_mutex_lock (&self->client_mutex_refine);
                pk_client_set_interactive (self->client_refine, gs_plugin_has_flags (plugin, 
GS_PLUGIN_FLAGS_INTERACTIVE));
-               results = pk_client_get_updates (self->client_refine,
-                                                filter,
-                                                cancellable,
-                                                gs_packagekit_helper_cb, helper,
-                                                error);
+               pk_client_get_updates_async (self->client_refine,
+                                            filter,
+                                            cancellable,
+                                            /* TODO: @helper is leaked here; this will be reworked in a 
subsequent commit */
+                                            gs_packagekit_helper_cb, g_object_ref (helper),
+                                            get_updates_cb,
+                                            refine_task_add_operation (task));
                g_mutex_unlock (&self->client_mutex_refine);
-               if (!gs_plugin_packagekit_results_valid (results, error)) {
-                       g_prefix_error (error, "failed to get updates for urgency: ");
-                       return FALSE;
-               }
-
-               /* set the update severity for the app */
-               sack = pk_results_get_package_sack (results);
-               for (guint i = 0; i < gs_app_list_length (list); i++) {
-                       g_autoptr (PkPackage) pkg = NULL;
-                       app = gs_app_list_index (list, i);
-                       if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD))
-                               continue;
-                       package_id = gs_app_get_source_id_default (app);
-                       if (package_id == NULL)
-                               continue;
-                       pkg = pk_package_sack_find_by_id (sack, package_id);
-                       if (pkg == NULL)
-                               continue;
-                       #ifdef HAVE_PK_PACKAGE_GET_UPDATE_SEVERITY
-                       switch (pk_package_get_update_severity (pkg)) {
-                       case PK_INFO_ENUM_LOW:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_LOW);
-                               break;
-                       case PK_INFO_ENUM_NORMAL:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_MEDIUM);
-                               break;
-                       case PK_INFO_ENUM_IMPORTANT:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_HIGH);
-                               break;
-                       case PK_INFO_ENUM_CRITICAL:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_CRITICAL);
-                               break;
-                       default:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_UNKNOWN);
-                               break;
-                       }
-                       #else
-                       switch (pk_package_get_info (pkg)) {
-                       case PK_INFO_ENUM_AVAILABLE:
-                       case PK_INFO_ENUM_NORMAL:
-                       case PK_INFO_ENUM_LOW:
-                       case PK_INFO_ENUM_ENHANCEMENT:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_LOW);
-                               break;
-                       case PK_INFO_ENUM_BUGFIX:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_MEDIUM);
-                               break;
-                       case PK_INFO_ENUM_SECURITY:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_CRITICAL);
-                               break;
-                       case PK_INFO_ENUM_IMPORTANT:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_HIGH);
-                               break;
-                       default:
-                               gs_app_set_update_urgency (app, AS_URGENCY_KIND_UNKNOWN);
-                               g_warning ("unhandled info state %s",
-                                          pk_info_enum_to_string (pk_package_get_info (pkg)));
-                               break;
-                       }
-                       #endif
-               }
        }
 
        for (guint i = 0; i < gs_app_list_length (list); i++) {
@@ -1565,21 +1538,347 @@ gs_plugin_refine (GsPlugin *plugin,
 
        /* add any missing history data */
        if (gs_app_list_length (history_list) > 0) {
-               /* FIXME: This will be made async shortly */
-               g_autoptr(GAsyncResult) async_result = NULL;
                gs_plugin_packagekit_refine_history_async (self,
                                                           history_list,
                                                           cancellable,
-                                                          async_result_cb,
-                                                          &async_result);
-               while (async_result == NULL)
-                       g_main_context_iteration (NULL, TRUE);
-               if (!gs_plugin_packagekit_refine_history_finish (self, async_result, error))
-                       return FALSE;
+                                                          refine_all_history_cb,
+                                                          refine_task_add_operation (task));
        }
 
-       /* 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
+upgrade_system_cb (GObject      *source_object,
+                   GAsyncResult *result,
+                   gpointer      user_data)
+{
+       PkClient *client = PK_CLIENT (source_object);
+       g_autoptr(GTask) refine_task = g_steal_pointer (&user_data);
+       GsPluginPackagekit *self = GS_PLUGIN_PACKAGEKIT (g_task_get_source_object (refine_task));
+       RefineData *data = g_task_get_task_data (refine_task);
+       g_autoptr(PkResults) results = NULL;
+       g_autoptr(GsAppList) results_list = 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 refine distro upgrade: ");
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       results_list = gs_app_list_new ();
+       if (!gs_plugin_packagekit_add_results (GS_PLUGIN (self), results_list, results, &local_error)) {
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       /* add each of these as related applications */
+       for (guint j = 0; j < gs_app_list_length (results_list); j++) {
+               GsApp *app2 = gs_app_list_index (results_list, j);
+               if (gs_app_get_state (app2) != GS_APP_STATE_UNAVAILABLE)
+                       continue;
+               gs_app_add_related (data->app_operating_system, app2);
+       }
+
+       refine_task_complete_operation (refine_task);
+}
+
+static gboolean
+gs_plugin_packagekit_refine_finish (GsPlugin      *plugin,
+                                    GAsyncResult  *result,
+                                    GError       **error)
+{
+       return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void resolve_all_packages_with_filter_cb2 (GObject      *source_object,
+                                                  GAsyncResult *result,
+                                                  gpointer      user_data);
+
+static void
+resolve_all_packages_with_filter_cb (GObject      *source_object,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+       GsPluginPackagekit *self = GS_PLUGIN_PACKAGEKIT (source_object);
+       g_autoptr(GTask) refine_task = g_steal_pointer (&user_data);
+       RefineData *data = g_task_get_task_data (refine_task);
+       GCancellable *cancellable = g_task_get_cancellable (refine_task);
+       GsAppList *resolve_list = data->resolve_list;
+       g_autoptr(GsAppList) resolve2_list = NULL;
+       PkBitfield filter;
+       g_autoptr(GError) local_error = NULL;
+
+       if (!gs_plugin_packagekit_resolve_packages_with_filter_finish (self,
+                                                                      result,
+                                                                      &local_error)) {
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       /* if any packages remaining in UNKNOWN state, try to resolve them again,
+        * but this time without ARCH filter */
+       resolve2_list = gs_app_list_new ();
+       for (guint i = 0; i < gs_app_list_length (resolve_list); i++) {
+               GsApp *app = gs_app_list_index (resolve_list, i);
+               if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN)
+                       gs_app_list_add (resolve2_list, app);
+       }
+       filter = pk_bitfield_from_enums (PK_FILTER_ENUM_NEWEST,
+                                        PK_FILTER_ENUM_NOT_ARCH,
+                                        PK_FILTER_ENUM_NOT_SOURCE,
+                                        -1);
+
+       gs_plugin_packagekit_resolve_packages_with_filter_async (self,
+                                                                resolve2_list,
+                                                                filter,
+                                                                cancellable,
+                                                                resolve_all_packages_with_filter_cb2,
+                                                                g_steal_pointer (&refine_task));
+}
+
+static void
+resolve_all_packages_with_filter_cb2 (GObject      *source_object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+       GsPluginPackagekit *self = GS_PLUGIN_PACKAGEKIT (source_object);
+       g_autoptr(GTask) refine_task = g_steal_pointer (&user_data);
+       g_autoptr(GError) local_error = NULL;
+
+       if (!gs_plugin_packagekit_resolve_packages_with_filter_finish (self,
+                                                                      result,
+                                                                      &local_error)) {
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       refine_task_complete_operation (refine_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;
+       GsPluginPackagekit *self = GS_PLUGIN_PACKAGEKIT (g_task_get_source_object (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;
+               package = g_ptr_array_index (packages, 0);
+               gs_plugin_packagekit_set_metadata_from_package (GS_PLUGIN (self), search_files_data->app, 
package);
+       } else {
+               g_warning ("Failed to find one package for %s, %s, [%u]",
+                          gs_app_get_id (search_files_data->app), search_files_data->filename, 
packages->len);
+       }
+
+       refine_task_complete_operation (refine_task);
+}
+
+static void
+get_update_detail_cb (GObject      *source_object,
+                      GAsyncResult *result,
+                      gpointer      user_data)
+{
+       PkClient *client = PK_CLIENT (source_object);
+       g_autoptr(GTask) refine_task = g_steal_pointer (&user_data);
+       RefineData *data = g_task_get_task_data (refine_task);
+       g_autoptr(PkResults) results = NULL;
+       g_autoptr(GPtrArray) array = 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 get update details: ");
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       /* set the update details for the update */
+       array = pk_results_get_update_detail_array (results);
+       for (guint j = 0; j < gs_app_list_length (data->update_details_list); j++) {
+               GsApp *app = gs_app_list_index (data->update_details_list, j);
+               const gchar *package_id = gs_app_get_source_id_default (app);
+
+               for (guint i = 0; i < array->len; i++) {
+                       const gchar *tmp;
+                       g_autofree gchar *desc = NULL;
+                       PkUpdateDetail *update_detail;
+
+                       /* right package? */
+                       update_detail = g_ptr_array_index (array, i);
+                       if (g_strcmp0 (package_id, pk_update_detail_get_package_id (update_detail)) != 0)
+                               continue;
+                       tmp = pk_update_detail_get_update_text (update_detail);
+                       desc = gs_plugin_packagekit_fixup_update_description (tmp);
+                       if (desc != NULL)
+                               gs_app_set_update_details_markup (app, desc);
+                       break;
+               }
+       }
+
+       refine_task_complete_operation (refine_task);
+}
+
+static void
+get_details_cb (GObject      *source_object,
+                GAsyncResult *result,
+                gpointer      user_data)
+{
+       PkClient *client = PK_CLIENT (source_object);
+       g_autoptr(GTask) refine_task = g_steal_pointer (&user_data);
+       GsPluginPackagekit *self = GS_PLUGIN_PACKAGEKIT (g_task_get_source_object (refine_task));
+       RefineData *data = g_task_get_task_data (refine_task);
+       g_autoptr(GPtrArray) array = NULL;
+       g_autoptr(PkResults) results = NULL;
+       g_autoptr(GHashTable) details_collection = 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_autoptr(GPtrArray) package_ids = app_list_get_package_ids (data->details_list, NULL, FALSE);
+               g_autofree gchar *package_ids_str = g_strjoinv (",", (gchar **) package_ids->pdata);
+               g_prefix_error (&local_error, "failed to get details for %s: ",
+                               package_ids_str);
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       /* get the results and copy them into a hash table for fast lookups:
+        * there are typically 400 to 700 elements in @array, and 100 to 200
+        * elements in @list, each with 1 or 2 source IDs to look up (but
+        * sometimes 200) */
+       array = pk_results_get_details_array (results);
+       details_collection = gs_plugin_packagekit_details_array_to_hash (array);
+
+       /* set the update details for the update */
+       for (guint i = 0; i < gs_app_list_length (data->details_list); i++) {
+               GsApp *app = gs_app_list_index (data->details_list, i);
+               gs_plugin_packagekit_refine_details_app (GS_PLUGIN (self), details_collection, app);
+       }
+
+       refine_task_complete_operation (refine_task);
+}
+
+static void
+get_updates_cb (GObject      *source_object,
+                GAsyncResult *result,
+                gpointer      user_data)
+{
+       PkClient *client = PK_CLIENT (source_object);
+       g_autoptr(GTask) refine_task = g_steal_pointer (&user_data);
+       RefineData *data = g_task_get_task_data (refine_task);
+       g_autoptr(PkPackageSack) sack = NULL;
+       g_autoptr(PkResults) results = 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 get updates for urgency: ");
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       /* set the update severity for the app */
+       sack = pk_results_get_package_sack (results);
+       for (guint i = 0; i < gs_app_list_length (data->full_list); i++) {
+               g_autoptr(PkPackage) pkg = NULL;
+               const gchar *package_id;
+               GsApp *app = gs_app_list_index (data->full_list, i);
+
+               if (gs_app_has_quirk (app, GS_APP_QUIRK_IS_WILDCARD))
+                       continue;
+               package_id = gs_app_get_source_id_default (app);
+               if (package_id == NULL)
+                       continue;
+               pkg = pk_package_sack_find_by_id (sack, package_id);
+               if (pkg == NULL)
+                       continue;
+               #ifdef HAVE_PK_PACKAGE_GET_UPDATE_SEVERITY
+               switch (pk_package_get_update_severity (pkg)) {
+               case PK_INFO_ENUM_LOW:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_LOW);
+                       break;
+               case PK_INFO_ENUM_NORMAL:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_MEDIUM);
+                       break;
+               case PK_INFO_ENUM_IMPORTANT:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_HIGH);
+                       break;
+               case PK_INFO_ENUM_CRITICAL:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_CRITICAL);
+                       break;
+               default:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_UNKNOWN);
+                       break;
+               }
+               #else
+               switch (pk_package_get_info (pkg)) {
+               case PK_INFO_ENUM_AVAILABLE:
+               case PK_INFO_ENUM_NORMAL:
+               case PK_INFO_ENUM_LOW:
+               case PK_INFO_ENUM_ENHANCEMENT:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_LOW);
+                       break;
+               case PK_INFO_ENUM_BUGFIX:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_MEDIUM);
+                       break;
+               case PK_INFO_ENUM_SECURITY:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_CRITICAL);
+                       break;
+               case PK_INFO_ENUM_IMPORTANT:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_HIGH);
+                       break;
+               default:
+                       gs_app_set_update_urgency (app, AS_URGENCY_KIND_UNKNOWN);
+                       g_warning ("unhandled info state %s",
+                                  pk_info_enum_to_string (pk_package_get_info (pkg)));
+                       break;
+               }
+               #endif
+       }
+
+       refine_task_complete_operation (refine_task);
+}
+
+static void
+refine_all_history_cb (GObject      *source_object,
+                       GAsyncResult *result,
+                       gpointer      user_data)
+{
+       GsPluginPackagekit *self = GS_PLUGIN_PACKAGEKIT (source_object);
+       g_autoptr(GTask) refine_task = g_steal_pointer (&user_data);
+       g_autoptr(GError) local_error = NULL;
+
+       if (!gs_plugin_packagekit_refine_history_finish (self, result, &local_error)) {
+               refine_task_complete_operation_with_error (refine_task, g_steal_pointer (&local_error));
+               return;
+       }
+
+       refine_task_complete_operation (refine_task);
 }
 
 static void
@@ -2853,6 +3152,8 @@ gs_plugin_packagekit_class_init (GsPluginPackagekitClass *klass)
        plugin_class->setup_finish = gs_plugin_packagekit_setup_finish;
        plugin_class->shutdown_async = gs_plugin_packagekit_shutdown_async;
        plugin_class->shutdown_finish = gs_plugin_packagekit_shutdown_finish;
+       plugin_class->refine_async = gs_plugin_packagekit_refine_async;
+       plugin_class->refine_finish = gs_plugin_packagekit_refine_finish;
 }
 
 GType


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