[gnome-software/1276-software-became-unresponsive-while-updating-flatpaks: 26/26] gs-plugin-loader: Convert app_create()/get_system_app() into async methods




commit 30b835e2a78bef438f1b572617bbf4d4552d3138
Author: Milan Crha <mcrha redhat com>
Date:   Tue Jul 20 21:04:59 2021 +0200

    gs-plugin-loader: Convert app_create()/get_system_app() into async methods
    
    These call refine, which can mean I/O calls on the calling thread,
    which is, in some situations, the main/GUI thread, thus it could
    block the application in certain cases. Doing the calls in a dedicated
    thread helps to avoid application freeze.
    
    Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1276

 lib/gs-plugin-loader-sync.c    |  72 +++++++++++++++++
 lib/gs-plugin-loader-sync.h    |   7 ++
 lib/gs-plugin-loader.c         | 174 ++++++++++++++++++++++++++++++++---------
 lib/gs-plugin-loader.h         |  18 ++++-
 plugins/core/gs-self-test.c    |  13 ++-
 plugins/flatpak/gs-self-test.c |  11 ++-
 src/gs-application.c           |  76 +++++++++++++++---
 src/gs-search-page.c           |  21 ++++-
 src/gs-update-monitor.c        |  48 ++++++------
 src/gs-updates-page.c          | 119 ++++++++++++++++++----------
 10 files changed, 437 insertions(+), 122 deletions(-)
---
diff --git a/lib/gs-plugin-loader-sync.c b/lib/gs-plugin-loader-sync.c
index bf83fe224..f7b0d0f50 100644
--- a/lib/gs-plugin-loader-sync.c
+++ b/lib/gs-plugin-loader-sync.c
@@ -178,3 +178,75 @@ gs_plugin_loader_job_process_app (GsPluginLoader *plugin_loader,
 
        return app;
 }
+
+GsApp *
+gs_plugin_loader_app_create (GsPluginLoader *plugin_loader,
+                            const gchar *unique_id,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       GsPluginLoaderHelper helper;
+       GsApp *app;
+
+       /* create temp object */
+       helper.res = NULL;
+       helper.context = g_main_context_new ();
+       helper.loop = g_main_loop_new (helper.context, FALSE);
+
+       g_main_context_push_thread_default (helper.context);
+
+       /* run async method */
+       gs_plugin_loader_app_create_async (plugin_loader,
+                                          unique_id,
+                                          cancellable,
+                                          _helper_finish_sync,
+                                          &helper);
+       g_main_loop_run (helper.loop);
+       app = gs_plugin_loader_app_create_finish (plugin_loader,
+                                                 helper.res,
+                                                 error);
+
+       g_main_context_pop_thread_default (helper.context);
+
+       g_main_loop_unref (helper.loop);
+       g_main_context_unref (helper.context);
+       if (helper.res != NULL)
+               g_object_unref (helper.res);
+
+       return app;
+}
+
+GsApp *
+gs_plugin_loader_get_system_app (GsPluginLoader *plugin_loader,
+                                GCancellable *cancellable,
+                                GError **error)
+{
+       GsPluginLoaderHelper helper;
+       GsApp *app;
+
+       /* create temp object */
+       helper.res = NULL;
+       helper.context = g_main_context_new ();
+       helper.loop = g_main_loop_new (helper.context, FALSE);
+
+       g_main_context_push_thread_default (helper.context);
+
+       /* run async method */
+       gs_plugin_loader_get_system_app_async (plugin_loader,
+                                              cancellable,
+                                              _helper_finish_sync,
+                                              &helper);
+       g_main_loop_run (helper.loop);
+       app = gs_plugin_loader_get_system_app_finish (plugin_loader,
+                                                     helper.res,
+                                                     error);
+
+       g_main_context_pop_thread_default (helper.context);
+
+       g_main_loop_unref (helper.loop);
+       g_main_context_unref (helper.context);
+       if (helper.res != NULL)
+               g_object_unref (helper.res);
+
+       return app;
+}
diff --git a/lib/gs-plugin-loader-sync.h b/lib/gs-plugin-loader-sync.h
index b428ba9f1..493effd0e 100644
--- a/lib/gs-plugin-loader-sync.h
+++ b/lib/gs-plugin-loader-sync.h
@@ -30,5 +30,12 @@ GPtrArray    *gs_plugin_loader_job_get_categories    (GsPluginLoader *plugin_loader,
                                                         GsPluginJob    *plugin_job,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+GsApp          *gs_plugin_loader_app_create            (GsPluginLoader *plugin_loader,
+                                                        const gchar    *unique_id,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
+GsApp          *gs_plugin_loader_get_system_app        (GsPluginLoader *plugin_loader,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 
 G_END_DECLS
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index 474aad154..54d27a53a 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -2318,24 +2318,19 @@ gs_plugin_loader_plugin_sort_fn (gconstpointer a, gconstpointer b)
 }
 
 static void
-gs_plugin_loader_plugin_dir_changed_cb (GFileMonitor *monitor,
-                                       GFile *file,
-                                       GFile *other_file,
-                                       GFileMonitorEvent event_type,
-                                       GsPluginLoader *plugin_loader)
+gs_plugin_loader_software_app_created_cb (GObject *source_object,
+                                         GAsyncResult *result,
+                                         gpointer user_data)
 {
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
        g_autoptr(GsApp) app = NULL;
        g_autoptr(GsPluginEvent) event = gs_plugin_event_new ();
        g_autoptr(GError) error = NULL;
 
-       /* already triggered */
-       if (plugin_loader->plugin_dir_dirty)
-               return;
+       app = gs_plugin_loader_app_create_finish (plugin_loader, result, NULL);
 
        /* add app */
        gs_plugin_event_set_action (event, GS_PLUGIN_ACTION_SETUP);
-       app = gs_plugin_loader_app_create (plugin_loader,
-               "system/*/*/org.gnome.Software.desktop/*");
        if (app != NULL)
                gs_plugin_event_set_app (event, app);
 
@@ -2346,6 +2341,22 @@ gs_plugin_loader_plugin_dir_changed_cb (GFileMonitor *monitor,
                             "A restart is required");
        gs_plugin_event_set_error (event, error);
        gs_plugin_loader_add_event (plugin_loader, event);
+}
+
+static void
+gs_plugin_loader_plugin_dir_changed_cb (GFileMonitor *monitor,
+                                       GFile *file,
+                                       GFile *other_file,
+                                       GFileMonitorEvent event_type,
+                                       GsPluginLoader *plugin_loader)
+{
+       /* already triggered */
+       if (plugin_loader->plugin_dir_dirty)
+               return;
+
+       gs_plugin_loader_app_create_async (plugin_loader, "system/*/*/org.gnome.Software.desktop/*",
+               NULL, gs_plugin_loader_software_app_created_cb, NULL);
+
        plugin_loader->plugin_dir_dirty = TRUE;
 }
 
@@ -3932,25 +3943,21 @@ gs_plugin_loader_get_plugin_supported (GsPluginLoader *plugin_loader,
        return FALSE;
 }
 
-/**
- * gs_plugin_loader_app_create:
- * @plugin_loader: a #GsPluginLoader
- * @unique_id: a unique_id
- *
- * Returns an application from the global cache, creating if required.
- *
- * Returns: (transfer full): a #GsApp
- **/
-GsApp *
-gs_plugin_loader_app_create (GsPluginLoader *plugin_loader, const gchar *unique_id)
+static void
+gs_plugin_loader_job_app_create_thread_cb (GTask *task,
+                                          gpointer object,
+                                          gpointer task_data,
+                                          GCancellable *cancellable)
 {
-       g_autoptr(GError) error = NULL;
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (g_task_get_source_object (task));
+       const gchar *unique_id = task_data;
+       GError *error = NULL;
        g_autoptr(GsApp) app = NULL;
        g_autoptr(GsAppList) list = gs_app_list_new ();
        g_autoptr(GsPluginJob) plugin_job = NULL;
        g_autoptr(GsPluginLoaderHelper) helper = NULL;
 
-       /* use the plugin loader to convert a wildcard app*/
+       /* use the plugin loader to convert a wildcard app */
        app = gs_app_new (NULL);
        gs_app_add_quirk (app, GS_APP_QUIRK_IS_WILDCARD);
        gs_app_set_from_unique_id (app, unique_id, AS_COMPONENT_KIND_UNKNOWN);
@@ -3958,15 +3965,18 @@ gs_plugin_loader_app_create (GsPluginLoader *plugin_loader, const gchar *unique_
        plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFINE, NULL);
        helper = gs_plugin_loader_helper_new (plugin_loader, plugin_job);
        if (!gs_plugin_loader_run_refine (helper, list, NULL, &error)) {
-               g_warning ("%s", error->message);
-               return NULL;
+               g_prefix_error (&error, "Failed to refine '%s': ", unique_id);
+               g_task_return_error (task, error);
+               return;
        }
 
        /* return the matching GsApp */
        for (guint i = 0; i < gs_app_list_length (list); i++) {
                GsApp *app_tmp = gs_app_list_index (list, i);
-               if (g_strcmp0 (unique_id, gs_app_get_unique_id (app_tmp)) == 0)
-                       return g_object_ref (app_tmp);
+               if (g_strcmp0 (unique_id, gs_app_get_unique_id (app_tmp)) == 0) {
+                       g_task_return_pointer (task, g_object_ref (app_tmp), g_object_unref);
+                       return;
+               }
        }
 
        /* return the first returned app that's not a wildcard */
@@ -3975,27 +3985,121 @@ gs_plugin_loader_app_create (GsPluginLoader *plugin_loader, const gchar *unique_
                if (!gs_app_has_quirk (app_tmp, GS_APP_QUIRK_IS_WILDCARD)) {
                        g_debug ("returning imperfect match: %s != %s",
                                 unique_id, gs_app_get_unique_id (app_tmp));
-                       return g_object_ref (app_tmp);
+                       g_task_return_pointer (task, g_object_ref (app_tmp), g_object_unref);
+                       return;
                }
        }
 
        /* does not exist */
-       g_warning ("failed to create an app for %s", unique_id);
-       return NULL;
+       g_task_return_new_error (task,
+                                GS_PLUGIN_ERROR,
+                                GS_PLUGIN_ERROR_FAILED,
+                                "Failed to create an app for '%s'", unique_id);
+}
+
+/**
+ * gs_plugin_loader_app_create_async:
+ * @plugin_loader: a #GsPluginLoader
+ * @unique_id: a unique_id
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: function to call when complete
+ * @user_data: user data to pass to @callback
+ *
+ * Create a #GsApp identified by @unique_id asynchronously.
+ * Finish the call with gs_plugin_loader_app_create_finish().
+ *
+ * Since: 41
+ **/
+void
+gs_plugin_loader_app_create_async (GsPluginLoader *plugin_loader,
+                                  const gchar *unique_id,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+       g_autoptr(GsPluginJob) plugin_job = NULL;
+       g_autoptr(GTask) task = NULL;
+
+       g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+       g_return_if_fail (unique_id != NULL);
+       g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+       /* run in a thread */
+       task = g_task_new (plugin_loader, cancellable, callback, user_data);
+       g_task_set_source_tag (task, gs_plugin_loader_app_create_async);
+       g_task_set_task_data (task, g_strdup (unique_id), g_free);
+       g_task_run_in_thread (task, gs_plugin_loader_job_app_create_thread_cb);
+}
+
+/**
+ * gs_plugin_loader_app_create_finish:
+ * @plugin_loader: a #GsPluginLoader
+ * @res: a #GAsyncResult
+ * @error: A #GError, or %NULL
+ *
+ * Finishes call to gs_plugin_loader_app_create_async().
+ *
+ * Returns: (transfer full): a #GsApp, or %NULL on error.
+ *
+ * Since: 41
+ **/
+GsApp *
+gs_plugin_loader_app_create_finish (GsPluginLoader *plugin_loader,
+                                   GAsyncResult *res,
+                                   GError **error)
+{
+       GsApp *app;
+
+       g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
+       g_return_val_if_fail (G_IS_TASK (res), NULL);
+       g_return_val_if_fail (g_task_is_valid (res, plugin_loader), NULL);
+       g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+       app = g_task_propagate_pointer (G_TASK (res), error);
+       gs_utils_error_convert_gio (error);
+       return app;
+}
+
+/**
+ * gs_plugin_loader_get_system_app_async:
+ * @plugin_loader: a #GsPluginLoader
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: function to call when complete
+ * @user_data: user data to pass to @callback
+ *
+ * Get the application that represents the currently installed OS
+ * asynchronously. Finish the call with gs_plugin_loader_get_system_app_finish().
+ *
+ * Since: 41
+ **/
+void
+gs_plugin_loader_get_system_app_async (GsPluginLoader *plugin_loader,
+                                      GCancellable *cancellable,
+                                      GAsyncReadyCallback callback,
+                                      gpointer user_data)
+{
+       gs_plugin_loader_app_create_async (plugin_loader, "*/*/*/system/*", cancellable, callback, user_data);
 }
 
 /**
- * gs_plugin_loader_get_system_app:
+ * gs_plugin_loader_get_system_app_finish:
  * @plugin_loader: a #GsPluginLoader
+ * @res: a #GAsyncResult
+ * @error: A #GError, or %NULL
+ *
+ * Finishes call to gs_plugin_loader_get_system_app_async().
  *
- * Returns the application that represents the currently installed OS.
+ * Returns: (transfer full): a #GsApp, which represents
+ *    the currently installed OS, or %NULL on error.
  *
- * Returns: (transfer full): a #GsApp
+ * Since: 41
  **/
 GsApp *
-gs_plugin_loader_get_system_app (GsPluginLoader *plugin_loader)
+gs_plugin_loader_get_system_app_finish (GsPluginLoader *plugin_loader,
+                                       GAsyncResult *res,
+                                       GError **error)
 {
-       return gs_plugin_loader_app_create (plugin_loader, "*/*/*/system/*");
+       return gs_plugin_loader_app_create_finish (plugin_loader, res, error);
 }
 
 /**
diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h
index 960795598..71fd35af8 100644
--- a/lib/gs-plugin-loader.h
+++ b/lib/gs-plugin-loader.h
@@ -70,9 +70,21 @@ GPtrArray    *gs_plugin_loader_get_events            (GsPluginLoader *plugin_loader);
 GsPluginEvent  *gs_plugin_loader_get_event_default     (GsPluginLoader *plugin_loader);
 void            gs_plugin_loader_remove_events         (GsPluginLoader *plugin_loader);
 
-GsApp          *gs_plugin_loader_app_create            (GsPluginLoader *plugin_loader,
-                                                        const gchar    *unique_id);
-GsApp          *gs_plugin_loader_get_system_app        (GsPluginLoader *plugin_loader);
+void            gs_plugin_loader_app_create_async      (GsPluginLoader *plugin_loader,
+                                                        const gchar    *unique_id,
+                                                        GCancellable   *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer        user_data);
+GsApp          *gs_plugin_loader_app_create_finish     (GsPluginLoader *plugin_loader,
+                                                        GAsyncResult   *res,
+                                                        GError         **error);
+void            gs_plugin_loader_get_system_app_async  (GsPluginLoader *plugin_loader,
+                                                        GCancellable   *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer        user_data);
+GsApp          *gs_plugin_loader_get_system_app_finish (GsPluginLoader *plugin_loader,
+                                                        GAsyncResult   *res,
+                                                        GError         **error);
 SoupSession    *gs_plugin_loader_get_soup_session      (GsPluginLoader *plugin_loader);
 GsOdrsProvider *gs_plugin_loader_get_odrs_provider     (GsPluginLoader *plugin_loader);
 
diff --git a/plugins/core/gs-self-test.c b/plugins/core/gs-self-test.c
index 53be465a9..dac3925d7 100644
--- a/plugins/core/gs-self-test.c
+++ b/plugins/core/gs-self-test.c
@@ -29,7 +29,9 @@ gs_plugins_core_search_repo_name_func (GsPluginLoader *plugin_loader)
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* force this app to be installed */
-       app_tmp = gs_plugin_loader_app_create (plugin_loader, "*/*/yellow/arachne.desktop/*");
+       app_tmp = gs_plugin_loader_app_create (plugin_loader, "*/*/yellow/arachne.desktop/*", NULL, &error);
+       g_assert_no_error (error);
+       g_assert_nonnull (app_tmp);
        gs_app_set_state (app_tmp, GS_APP_STATE_INSTALLED);
 
        /* get search result based on addon keyword */
@@ -63,7 +65,9 @@ gs_plugins_core_os_release_func (GsPluginLoader *plugin_loader)
        gs_plugin_loader_setup_again (plugin_loader);
 
        /* refine system application */
-       app = gs_plugin_loader_get_system_app (plugin_loader);
+       app = gs_plugin_loader_get_system_app (plugin_loader, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_nonnull (app);
        plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFINE,
                                         "app", app,
                                         "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_URL |
@@ -89,8 +93,9 @@ gs_plugins_core_os_release_func (GsPluginLoader *plugin_loader)
        g_assert_cmpstr (gs_app_get_summary (app), ==, "Fedora Workstation");
 
        /* check we can get this by the old name too */
-       app3 = gs_plugin_loader_get_system_app (plugin_loader);
-       g_assert (app3 != NULL);
+       app3 = gs_plugin_loader_get_system_app (plugin_loader, NULL, &error);
+       g_assert_no_error (error);
+       g_assert_nonnull (app3);
        g_assert (app3 == app);
 }
 
diff --git a/plugins/flatpak/gs-self-test.c b/plugins/flatpak/gs-self-test.c
index e17eaf385..a19ed4b6c 100644
--- a/plugins/flatpak/gs-self-test.c
+++ b/plugins/flatpak/gs-self-test.c
@@ -1696,8 +1696,12 @@ gs_plugins_flatpak_runtime_extension_func (GsPluginLoader *plugin_loader)
 
        /* check if the extension was installed */
        extension = gs_plugin_loader_app_create (plugin_loader,
-                       "user/flatpak/*/org.test.Chiron.Extension/master");
+                       "user/flatpak/*/org.test.Chiron.Extension/master",
+                       NULL, &error);
+       gs_test_flush_main_context ();
+       g_assert_no_error (error);
        g_assert_nonnull (extension);
+
        g_assert_cmpint (gs_app_get_state (extension), ==, GS_APP_STATE_INSTALLED);
 
        /* switch to the new repo (to get the update) */
@@ -1782,7 +1786,10 @@ gs_plugins_flatpak_runtime_extension_func (GsPluginLoader *plugin_loader)
        /* The install refreshes GsApp-s cache, thus re-get the extension */
        g_clear_object (&extension);
        extension = gs_plugin_loader_app_create (plugin_loader,
-                       "user/flatpak/*/org.test.Chiron.Extension/master");
+                       "user/flatpak/*/org.test.Chiron.Extension/master",
+                       NULL, &error);
+       gs_test_flush_main_context ();
+       g_assert_no_error (error);
        g_assert_nonnull (extension);
 
        /* check the extension's state after the update */
diff --git a/src/gs-application.c b/src/gs-application.c
index 2ddfaadfc..40356f905 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -573,6 +573,28 @@ _search_launchable_details_cb (GObject *source, GAsyncResult *res, gpointer user
        gs_shell_show_app (app->shell, a);
 }
 
+static void
+gs_application_app_to_show_created_cb (GObject *source_object,
+                                      GAsyncResult *result,
+                                      gpointer user_data)
+{
+       GsApplication *gs_app = user_data;
+       g_autoptr(GsApp) app = NULL;
+       g_autoptr(GError) error = NULL;
+
+       app = gs_plugin_loader_app_create_finish (GS_PLUGIN_LOADER (source_object), result, &error);
+       if (app == NULL) {
+               if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+                   !g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED))
+                       g_warning ("Failed to create application: %s", error->message);
+       } else {
+               g_return_if_fail (GS_IS_APPLICATION (gs_app));
+
+               gs_shell_reset_state (gs_app->shell);
+               gs_shell_show_app (gs_app->shell, app);
+       }
+}
+
 static void
 details_activated (GSimpleAction *action,
                   GVariant      *parameter,
@@ -594,9 +616,8 @@ details_activated (GSimpleAction *action,
                g_autoptr(GsPluginJob) plugin_job = NULL;
                data_id = gs_utils_unique_id_compat_convert (id);
                if (data_id != NULL) {
-                       g_autoptr(GsApp) a = gs_plugin_loader_app_create (app->plugin_loader, data_id);
-                       gs_shell_reset_state (app->shell);
-                       gs_shell_show_app (app->shell, a);
+                       gs_plugin_loader_app_create_async (app->plugin_loader, data_id, app->cancellable,
+                               gs_application_app_to_show_created_cb, app);
                        return;
                }
 
@@ -656,6 +677,41 @@ details_url_activated (GSimpleAction *action,
        gs_shell_show_app (app->shell, a);
 }
 
+typedef struct {
+       GWeakRef gs_app_weakref;
+       gchar *data_id;
+       GsShellInteraction interaction;
+} InstallActivatedHelper;
+
+static void
+gs_application_app_to_install_created_cb (GObject *source_object,
+                                         GAsyncResult *result,
+                                         gpointer user_data)
+{
+       InstallActivatedHelper *helper = user_data;
+       g_autoptr(GsApp) app = NULL;
+       g_autoptr(GError) error = NULL;
+
+       app = gs_plugin_loader_app_create_finish (GS_PLUGIN_LOADER (source_object), result, &error);
+       if (app == NULL) {
+               if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+                   !g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED))
+                       g_warning ("Failed to create application '%s': %s", helper->data_id, error->message);
+       } else {
+               g_autoptr(GsApplication) gs_app = NULL;
+
+               gs_app = g_weak_ref_get (&helper->gs_app_weakref);
+               if (gs_app != NULL) {
+                       gs_shell_reset_state (gs_app->shell);
+                       gs_shell_install (gs_app->shell, app, helper->interaction);
+               }
+       }
+
+       g_weak_ref_clear (&helper->gs_app_weakref);
+       g_free (helper->data_id);
+       g_slice_free (InstallActivatedHelper, helper);
+}
+
 static void
 install_activated (GSimpleAction *action,
                   GVariant      *parameter,
@@ -664,6 +720,7 @@ install_activated (GSimpleAction *action,
        GsApplication *app = GS_APPLICATION (data);
        const gchar *id;
        GsShellInteraction interaction;
+       InstallActivatedHelper *helper;
        g_autoptr (GsApp) a = NULL;
        g_autofree gchar *data_id = NULL;
 
@@ -677,14 +734,13 @@ install_activated (GSimpleAction *action,
        if (interaction == GS_SHELL_INTERACTION_FULL)
                gs_application_present_window (app, NULL);
 
-       a = gs_plugin_loader_app_create (app->plugin_loader, data_id);
-       if (a == NULL) {
-               g_warning ("Could not create app from data-id: %s", data_id);
-               return;
-       }
+       helper = g_slice_new0 (InstallActivatedHelper);
+       g_weak_ref_init (&helper->gs_app_weakref, app);
+       helper->data_id = g_strdup (data_id);
+       helper->interaction = interaction;
 
-       gs_shell_reset_state (app->shell);
-       gs_shell_install (app->shell, a, interaction);
+       gs_plugin_loader_app_create_async (app->plugin_loader, data_id, app->cancellable,
+               gs_application_app_to_install_created_cb, helper);
 }
 
 static GFile *
diff --git a/src/gs-search-page.c b/src/gs-search-page.c
index abaa358a1..6b89edb50 100644
--- a/src/gs-search-page.c
+++ b/src/gs-search-page.c
@@ -81,6 +81,23 @@ gs_search_page_waiting_cancel (GsSearchPage *self)
        self->waiting_id = 0;
 }
 
+static void
+gs_search_page_app_to_show_created_cb (GObject *source_object,
+                                      GAsyncResult *result,
+                                      gpointer user_data)
+{
+       GsSearchPage *self = user_data;
+       g_autoptr(GsApp) app = NULL;
+       g_autoptr(GError) error = NULL;
+
+       app = gs_plugin_loader_app_create_finish (GS_PLUGIN_LOADER (source_object), result, &error);
+       if (app != NULL) {
+               g_return_if_fail (GS_IS_SEARCH_PAGE (self));
+
+               gs_shell_show_app (self->shell, app);
+       }
+}
+
 static void
 gs_search_page_get_search_cb (GObject *source_object,
                               GAsyncResult *res,
@@ -167,8 +184,8 @@ gs_search_page_get_search_cb (GObject *source_object,
        if (self->appid_to_show != NULL) {
                g_autoptr (GsApp) a = NULL;
                if (as_utils_data_id_valid (self->appid_to_show)) {
-                       a = gs_plugin_loader_app_create (self->plugin_loader,
-                                                        self->appid_to_show);
+                       gs_plugin_loader_app_create_async (self->plugin_loader, self->appid_to_show, 
self->cancellable,
+                               gs_search_page_app_to_show_created_cb, self);
                } else {
                        a = gs_app_new (self->appid_to_show);
                }
diff --git a/src/gs-update-monitor.c b/src/gs-update-monitor.c
index dd09b76d5..0f18eef0c 100644
--- a/src/gs-update-monitor.c
+++ b/src/gs-update-monitor.c
@@ -60,17 +60,28 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(DownloadUpdatesData, download_updates_data_free);
 typedef struct {
        GsUpdateMonitor         *monitor;
        GsApp                   *app;
-} LanguagePackData;
+} WithAppData;
+
+static WithAppData *
+with_app_data_new (GsUpdateMonitor     *monitor,
+                  GsApp                *app)
+{
+       WithAppData *data;
+       data = g_slice_new0 (WithAppData);
+       data->monitor = g_object_ref (monitor);
+       data->app = g_object_ref (app);
+       return data;
+}
 
 static void
-language_pack_data_free (LanguagePackData *data)
+with_app_data_free (WithAppData *data)
 {
        g_clear_object (&data->monitor);
        g_clear_object (&data->app);
-       g_slice_free (LanguagePackData, data);
+       g_slice_free (WithAppData, data);
 }
 
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(LanguagePackData, language_pack_data_free);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(WithAppData, with_app_data_free);
 
 static gboolean
 reenable_offline_update_notification (gpointer data)
@@ -612,13 +623,14 @@ static void
 get_system_finished_cb (GObject *object, GAsyncResult *res, gpointer data)
 {
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
-       GsUpdateMonitor *monitor = GS_UPDATE_MONITOR (data);
+       GsUpdateMonitor *monitor = data;
        g_autoptr(GError) error = NULL;
        g_autoptr(GNotification) n = NULL;
        g_autoptr(GsApp) app = NULL;
 
        /* get result */
-       if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) {
+       app = gs_plugin_loader_get_system_app_finish (plugin_loader, res, &error);
+       if (app == NULL) {
                if (!g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED))
                        g_warning ("failed to get system: %s", error->message);
                return;
@@ -632,7 +644,6 @@ get_system_finished_cb (GObject *object, GAsyncResult *res, gpointer data)
                return;
 
        /* is not EOL */
-       app = gs_plugin_loader_get_system_app (plugin_loader);
        if (gs_app_get_state (app) != GS_APP_STATE_UNAVAILABLE)
                return;
 
@@ -766,17 +777,10 @@ static void
 get_system (GsUpdateMonitor *monitor)
 {
        g_autoptr(GsApp) app = NULL;
-       g_autoptr(GsPluginJob) plugin_job = NULL;
 
        g_debug ("Getting system");
-       app = gs_plugin_loader_get_system_app (monitor->plugin_loader);
-       plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFINE,
-                                        "app", app,
-                                        NULL);
-       gs_plugin_loader_job_process_async (monitor->plugin_loader, plugin_job,
-                                           monitor->cancellable,
-                                           get_system_finished_cb,
-                                           monitor);
+       gs_plugin_loader_get_system_app_async (monitor->plugin_loader, monitor->cancellable,
+               get_system_finished_cb, monitor);
 }
 
 static void
@@ -816,7 +820,7 @@ static void
 install_language_pack_cb (GObject *object, GAsyncResult *res, gpointer data)
 {
        g_autoptr(GError) error = NULL;
-       g_autoptr(LanguagePackData) language_pack_data = data;
+       g_autoptr(WithAppData) with_app_data = data;
 
        if (!gs_plugin_loader_job_action_finish (GS_PLUGIN_LOADER (object), res, &error)) {
                if (!g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED))
@@ -824,7 +828,7 @@ install_language_pack_cb (GObject *object, GAsyncResult *res, gpointer data)
                return;
        } else {
                g_debug ("language pack for %s installed",
-                        gs_app_get_name (language_pack_data->app));
+                        gs_app_get_name (with_app_data->app));
        }
 }
 
@@ -852,12 +856,10 @@ get_language_pack_cb (GObject *object, GAsyncResult *res, gpointer data)
        /* there should be one langpack for a given locale */
        app = g_object_ref (gs_app_list_index (app_list, 0));
        if (!gs_app_is_installed (app)) {
-               g_autoptr(LanguagePackData) language_pack_data = NULL;
+               WithAppData *with_app_data;
                g_autoptr(GsPluginJob) plugin_job = NULL;
 
-               language_pack_data = g_slice_new0 (LanguagePackData);
-               language_pack_data->monitor = g_object_ref (monitor);
-               language_pack_data->app = g_object_ref (app);
+               with_app_data = with_app_data_new (monitor, app);
 
                plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_INSTALL,
                                                         "app", app,
@@ -866,7 +868,7 @@ get_language_pack_cb (GObject *object, GAsyncResult *res, gpointer data)
                                                    plugin_job,
                                                    monitor->cancellable,
                                                    install_language_pack_cb,
-                                                   g_steal_pointer (&language_pack_data));
+                                                   with_app_data);
        }
 }
 
diff --git a/src/gs-updates-page.c b/src/gs-updates-page.c
index a95ec461b..7219048ff 100644
--- a/src/gs-updates-page.c
+++ b/src/gs-updates-page.c
@@ -515,31 +515,52 @@ gs_updates_page_get_upgrades_cb (GObject *source_object,
        gs_updates_page_decrement_refresh_count (self);
 }
 
+typedef struct {
+       GsApp           *app; /* (owned) */
+       GsUpdatesPage   *self; /* (owned) */
+} GsPageHelper;
+
+static GsPageHelper *
+gs_page_helper_new (GsUpdatesPage *self,
+                   GsApp        *app)
+{
+       GsPageHelper *helper;
+       helper = g_slice_new0 (GsPageHelper);
+       helper->self = g_object_ref (self);
+       helper->app = g_object_ref (app);
+       return helper;
+}
+
 static void
-gs_updates_page_get_system_finished_cb (GObject *source_object,
-                                       GAsyncResult *res,
-                                       gpointer user_data)
+gs_page_helper_free (GsPageHelper *helper)
+{
+       g_clear_object (&helper->app);
+       g_clear_object (&helper->self);
+       g_slice_free (GsPageHelper, helper);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsPageHelper, gs_page_helper_free);
+
+static void
+gs_updates_page_refine_system_finished_cb (GObject *source_object,
+                                          GAsyncResult *res,
+                                          gpointer user_data)
 {
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
-       GsUpdatesPage *self = GS_UPDATES_PAGE (user_data);
+       g_autoptr(GsPageHelper) helper = user_data;
+       GsUpdatesPage *self = helper->self;
+       GsApp *app = helper->app;
        g_autoptr(GError) error = NULL;
-       g_autoptr(GsApp) app = NULL;
        g_autoptr(GString) str = g_string_new (NULL);
 
        /* get result */
        if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) {
                if (!g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED))
-                       g_warning ("failed to get system: %s", error->message);
+                       g_warning ("Failed to refine system: %s", error->message);
                return;
        }
 
        /* show or hide the end of life notification */
-       app = gs_plugin_loader_get_system_app (plugin_loader);
-       if (app == NULL) {
-               g_warning ("failed to get system app");
-               gtk_widget_set_visible (self->box_end_of_life, FALSE);
-               return;
-       }
        if (gs_app_get_state (app) != GS_APP_STATE_UNAVAILABLE) {
                gtk_widget_set_visible (self->box_end_of_life, FALSE);
                return;
@@ -570,6 +591,45 @@ gs_updates_page_get_system_finished_cb (GObject *source_object,
 
 }
 
+static void
+gs_updates_page_get_system_finished_cb (GObject *source_object,
+                                       GAsyncResult *res,
+                                       gpointer user_data)
+{
+       guint64 refine_flags;
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+       GsUpdatesPage *self = user_data;
+       GsPageHelper *helper;
+       g_autoptr(GsApp) app = NULL;
+       g_autoptr(GsPluginJob) plugin_job = NULL;
+       g_autoptr(GError) error = NULL;
+
+       app = gs_plugin_loader_get_system_app_finish (plugin_loader, res, &error);
+       if (app == NULL) {
+               if (!g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED))
+                       g_warning ("Failed to get system: %s", error->message);
+               return;
+       }
+
+       g_return_if_fail (GS_IS_UPDATES_PAGE (self));
+
+       refine_flags = GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON |
+                      GS_PLUGIN_REFINE_FLAGS_REQUIRE_SIZE |
+                      GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS |
+                      GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION;
+
+       helper = gs_page_helper_new (self, app);
+       plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFINE,
+                                        "interactive", TRUE,
+                                        "app", app,
+                                        "refine-flags", refine_flags,
+                                        NULL);
+       gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job,
+                                           self->cancellable,
+                                           gs_updates_page_refine_system_finished_cb,
+                                           helper);
+}
+
 static void
 gs_updates_page_load (GsUpdatesPage *self)
 {
@@ -601,17 +661,8 @@ gs_updates_page_load (GsUpdatesPage *self)
                                            self);
 
        /* get the system state */
-       g_object_unref (plugin_job);
-       app = gs_plugin_loader_get_system_app (self->plugin_loader);
-       plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFINE,
-                                        "interactive", TRUE,
-                                        "app", app,
-                                        "refine-flags", refine_flags,
-                                        NULL);
-       gs_plugin_loader_job_process_async (self->plugin_loader, plugin_job,
-                                           self->cancellable,
-                                           gs_updates_page_get_system_finished_cb,
-                                           self);
+       gs_plugin_loader_get_system_app_async (self->plugin_loader, self->cancellable,
+               gs_updates_page_get_system_finished_cb, self);
 
        /* don't refresh every each time */
        if ((self->result_flags & GS_UPDATES_PAGE_FLAG_HAS_UPGRADES) == 0) {
@@ -865,29 +916,13 @@ gs_updates_page_pending_apps_changed_cb (GsPluginLoader *plugin_loader,
        gs_updates_page_invalidate (self);
 }
 
-typedef struct {
-       GsApp           *app;
-       GsUpdatesPage   *self;
-} GsPageHelper;
-
-static void
-gs_page_helper_free (GsPageHelper *helper)
-{
-       if (helper->app != NULL)
-               g_object_unref (helper->app);
-       if (helper->self != NULL)
-               g_object_unref (helper->self);
-       g_slice_free (GsPageHelper, helper);
-}
-
-G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsPageHelper, gs_page_helper_free);
-
 static void
 upgrade_download_finished_cb (GObject *source,
                               GAsyncResult *res,
                               gpointer user_data)
 {
        GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+       g_autoptr(GsPageHelper) helper = user_data;
        g_autoptr(GError) error = NULL;
 
        if (!gs_plugin_loader_job_action_finish (plugin_loader, res, &error)) {
@@ -911,9 +946,7 @@ gs_updates_page_upgrade_download_cb (GsUpgradeBanner *upgrade_banner,
                return;
        }
 
-       helper = g_slice_new0 (GsPageHelper);
-       helper->app = g_object_ref (app);
-       helper->self = g_object_ref (self);
+       helper = gs_page_helper_new (self, app);
 
        if (self->cancellable_upgrade_download != NULL)
                g_object_unref (self->cancellable_upgrade_download);


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