[gnome-software: 6/8] gs-odrs-provider: Make refreshing asynchronous
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software: 6/8] gs-odrs-provider: Make refreshing asynchronous
- Date: Fri, 25 Feb 2022 12:33:49 +0000 (UTC)
commit 0fe8fffdb4ee87f03210fc067b08fa934864f7d4
Author: Philip Withnall <pwithnall endlessos org>
Date: Tue Feb 22 16:58:16 2022 +0000
gs-odrs-provider: Make refreshing asynchronous
Change the refresh API to be asynchronous, and to no longer rely on a
`GsPlugin` being passed in to provide progress updates.
This will allow `GsOdrsProvider` to be used in contexts where a
`GsPlugin` isn’t available, and it decouples progress updates from the
locking semantics of setting properties on a `GsApp`.
Signed-off-by: Philip Withnall <pwithnall endlessos org>
Helps: #1472
lib/gs-odrs-provider.c | 135 ++++++++++++++++++++++++++++++++++---------------
lib/gs-odrs-provider.h | 13 +++--
lib/gs-plugin-loader.c | 95 ++++++++++++++++++++++++++++++----
3 files changed, 189 insertions(+), 54 deletions(-)
---
diff --git a/lib/gs-odrs-provider.c b/lib/gs-odrs-provider.c
index 138e7ed3e..0286ddea4 100644
--- a/lib/gs-odrs-provider.c
+++ b/lib/gs-odrs-provider.c
@@ -1268,77 +1268,130 @@ gs_odrs_provider_new (const gchar *review_server,
NULL);
}
+static void download_ratings_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data);
+
/**
- * gs_odrs_provider_refresh:
+ * gs_odrs_provider_refresh_ratings_async:
* @self: a #GsOdrsProvider
- * @plugin: the #GsPlugin running this operation
* @cache_age_secs: cache age, in seconds, as passed to gs_plugin_refresh()
+ * @progress_callback: (nullable): callback to call with progress information
+ * @progress_user_data: (nullable) (closure progress_callback): data to pass
+ * to @progress_callback
* @cancellable: (nullable): a #GCancellable, or %NULL
- * @error: return location for a #GError
+ * @callback: function to call when the asynchronous operation is complete
+ * @user_data: data to pass to @callback
*
- * Refresh the cached ODRS ratings and re-load them.
+ * Refresh the cached ODRS ratings and re-load them asynchronously.
*
- * Returns: %TRUE on success, %FALSE otherwise
- * Since: 41
+ * Since: 42
*/
-gboolean
-gs_odrs_provider_refresh (GsOdrsProvider *self,
- GsPlugin *plugin,
- guint64 cache_age_secs,
- GCancellable *cancellable,
- GError **error)
+void
+gs_odrs_provider_refresh_ratings_async (GsOdrsProvider *self,
+ guint64 cache_age_secs,
+ GsDownloadProgressCallback progress_callback,
+ gpointer progress_user_data,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
g_autofree gchar *cache_filename = NULL;
+ g_autoptr(GFile) cache_file = NULL;
g_autofree gchar *uri = NULL;
g_autoptr(GError) error_local = NULL;
- g_autoptr(GsApp) app_dl = NULL;
+ g_autoptr(GTask) task = NULL;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_odrs_provider_refresh_ratings_async);
/* check cache age */
cache_filename = gs_utils_get_cache_filename ("odrs",
"ratings.json",
GS_UTILS_CACHE_FLAG_WRITEABLE |
GS_UTILS_CACHE_FLAG_CREATE_DIRECTORY,
- error);
- if (cache_filename == NULL)
- return FALSE;
+ &error_local);
+ if (cache_filename == NULL) {
+ g_task_return_error (task, g_steal_pointer (&error_local));
+ return;
+ }
+
+ cache_file = g_file_new_for_path (cache_filename);
+ g_task_set_task_data (task, g_object_ref (cache_file), g_object_unref);
+
if (cache_age_secs > 0) {
guint64 tmp;
- g_autoptr(GFile) file = NULL;
- file = g_file_new_for_path (cache_filename);
- tmp = gs_utils_get_file_age (file);
+
+ tmp = gs_utils_get_file_age (cache_file);
if (tmp < cache_age_secs) {
g_debug ("%s is only %" G_GUINT64_FORMAT " seconds old, so ignoring refresh",
cache_filename, tmp);
- return gs_odrs_provider_load_ratings (self, cache_filename, error);
+ if (!gs_odrs_provider_load_ratings (self, cache_filename, &error_local))
+ g_task_return_error (task, g_steal_pointer (&error_local));
+ else
+ g_task_return_boolean (task, TRUE);
+ return;
}
}
- app_dl = gs_app_new ("odrs");
-
/* download the complete file */
uri = g_strdup_printf ("%s/ratings", self->review_server);
g_debug ("Updating ODRS cache from %s to %s", uri, cache_filename);
- gs_app_set_summary_missing (app_dl,
- /* TRANSLATORS: status text when downloading */
- _("Downloading application ratings…"));
- if (!gs_plugin_download_file (plugin, app_dl, uri, cache_filename, cancellable, &error_local)) {
- g_autoptr(GsPluginEvent) event = NULL;
-
- event = gs_plugin_event_new ("error", error_local,
- "action", GS_PLUGIN_ACTION_DOWNLOAD,
- "origin", self->cached_origin,
- NULL);
-
- if (gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE))
- gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INTERACTIVE);
- else
- gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING);
- gs_plugin_report_event (plugin, event);
- /* don't fail updates if the ratings server is unavailable */
- return TRUE;
+ gs_download_file_async (self->session, uri, cache_file, G_PRIORITY_LOW,
+ progress_callback, progress_user_data,
+ cancellable, download_ratings_cb, g_steal_pointer (&task));
+}
+
+static void
+download_ratings_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ SoupSession *soup_session = SOUP_SESSION (source_object);
+ g_autoptr(GTask) task = g_steal_pointer (&user_data);
+ GsOdrsProvider *self = g_task_get_source_object (task);
+ GFile *cache_file = g_task_get_task_data (task);
+ g_autoptr(GError) local_error = NULL;
+
+ if (!gs_download_file_finish (soup_session, result, &local_error)) {
+ g_task_return_new_error (task, GS_ODRS_PROVIDER_ERROR,
+ GS_ODRS_PROVIDER_ERROR_DOWNLOADING,
+ "%s", local_error->message);
+ return;
}
- return gs_odrs_provider_load_ratings (self, cache_filename, error);
+
+ if (!gs_odrs_provider_load_ratings (self, g_file_peek_path (cache_file), &local_error))
+ g_task_return_new_error (task, GS_ODRS_PROVIDER_ERROR,
+ GS_ODRS_PROVIDER_ERROR_PARSING_DATA,
+ "%s", local_error->message);
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+/**
+ * gs_odrs_provider_refresh_ratings_finish:
+ * @self: a #GsOdrsProvider
+ * @result: result of the asynchronous operation
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finish an asynchronous refresh operation started with
+ * gs_odrs_provider_refresh_ratings_async().
+ *
+ * Returns: %TRUE on success, %FALSE otherwise
+ * Since: 42
+ */
+gboolean
+gs_odrs_provider_refresh_ratings_finish (GsOdrsProvider *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (GS_IS_ODRS_PROVIDER (self), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
+ g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) ==
gs_odrs_provider_refresh_ratings_async, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
}
/**
diff --git a/lib/gs-odrs-provider.h b/lib/gs-odrs-provider.h
index 90cc51b3d..2f045d2ed 100644
--- a/lib/gs-odrs-provider.h
+++ b/lib/gs-odrs-provider.h
@@ -14,7 +14,9 @@
#include <glib-object.h>
#include <libsoup/soup.h>
-#include <gs-plugin.h>
+#include "gs-app-list.h"
+#include "gs-download-utils.h"
+#include "gs-plugin-types.h"
G_BEGIN_DECLS
@@ -50,10 +52,15 @@ GsOdrsProvider *gs_odrs_provider_new (const gchar
*review_server,
guint n_results_max,
SoupSession *session);
-gboolean gs_odrs_provider_refresh (GsOdrsProvider *self,
- GsPlugin *plugin,
+void gs_odrs_provider_refresh_ratings_async (GsOdrsProvider *self,
guint64 cache_age_secs,
+ GsDownloadProgressCallback progress_callback,
+ gpointer progress_user_data,
GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean gs_odrs_provider_refresh_ratings_finish(GsOdrsProvider *self,
+ GAsyncResult *result,
GError **error);
gboolean gs_odrs_provider_refine (GsOdrsProvider *self,
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index 5b17e2677..4081a6910 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -83,6 +83,13 @@ struct _GsPluginLoader
static void gs_plugin_loader_monitor_network (GsPluginLoader *plugin_loader);
static void add_app_to_install_queue (GsPluginLoader *plugin_loader, GsApp *app);
static void gs_plugin_loader_process_in_thread_pool_cb (gpointer data, gpointer user_data);
+static void gs_plugin_loader_status_changed_cb (GsPlugin *plugin,
+ GsApp *app,
+ GsPluginStatus status,
+ GsPluginLoader *plugin_loader);
+static void async_result_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data);
G_DEFINE_TYPE (GsPluginLoader, gs_plugin_loader, G_TYPE_OBJECT)
@@ -863,6 +870,32 @@ gs_plugin_loader_job_sorted_truncation (GsPluginLoaderHelper *helper)
gs_app_list_truncate (list, max_results);
}
+typedef struct {
+ GsPluginLoader *plugin_loader; /* (not nullable) (unowned) */
+ GsApp *app; /* (not nullable) (unowned) */
+} OdrsRefreshProgressData;
+
+static void
+odrs_refresh_progress_cb (gsize bytes_downloaded,
+ gsize total_download_size,
+ gpointer user_data)
+{
+ OdrsRefreshProgressData *data = user_data;
+ guint percentage;
+
+ if (total_download_size > 0)
+ percentage = (guint) ((100 * bytes_downloaded) / total_download_size);
+ else
+ percentage = 0;
+
+ g_debug ("%s progress: %u%%", gs_app_get_id (data->app), percentage);
+ gs_app_set_progress (data->app, percentage);
+
+ gs_plugin_loader_status_changed_cb (NULL, data->app,
+ GS_PLUGIN_STATUS_DOWNLOADING,
+ data->plugin_loader);
+}
+
static gboolean
gs_plugin_loader_run_results (GsPluginLoaderHelper *helper,
GCancellable *cancellable,
@@ -906,17 +939,59 @@ gs_plugin_loader_run_results (GsPluginLoaderHelper *helper,
if (action == GS_PLUGIN_ACTION_REFRESH &&
plugin_loader->odrs_provider != NULL) {
- /* FIXME: Using plugin_loader->plugins->pdata[0] is a hack; the
- * GsOdrsProvider needs access to a GsPlugin to access global
- * state for gs_plugin_download_file(), but it doesn’t really
- * matter which plugin it’s accessed through. In lieu of
- * refactoring gs_plugin_download_file(), use the first plugin
- * in the list for now. */
- if (!gs_odrs_provider_refresh (plugin_loader->odrs_provider,
- plugin_loader->plugins->pdata[0],
- gs_plugin_job_get_age (helper->plugin_job),
- cancellable, error))
+ g_autoptr(GsApp) app_dl = NULL;
+ OdrsRefreshProgressData progress_data;
+ g_autoptr(GAsyncResult) odrs_result = NULL;
+ g_autoptr(GError) local_error = NULL;
+
+ app_dl = gs_app_new ("odrs");
+ gs_app_set_summary_missing (app_dl,
+ /* TRANSLATORS: status text when downloading */
+ _("Downloading application ratings…"));
+
+ progress_data.plugin_loader = plugin_loader;
+ progress_data.app = app_dl;
+
+ gs_odrs_provider_refresh_ratings_async (plugin_loader->odrs_provider,
+ gs_plugin_job_get_age (helper->plugin_job),
+ odrs_refresh_progress_cb,
+ &progress_data,
+ cancellable,
+ async_result_cb,
+ &odrs_result);
+
+ /* FIXME: Make this sync until the enclosing function is
+ * refactored to be async. */
+ while (odrs_result == NULL)
+ g_main_context_iteration (g_main_context_get_thread_default (), TRUE);
+
+ if (!gs_odrs_provider_refresh_ratings_finish (plugin_loader->odrs_provider,
+ odrs_result,
+ &local_error)) {
+ /* Don’t fail updates if the ratings server is unavailable */
+ if (g_error_matches (local_error, GS_ODRS_PROVIDER_ERROR,
+ GS_ODRS_PROVIDER_ERROR_DOWNLOADING) ||
+ g_error_matches (local_error, GS_ODRS_PROVIDER_ERROR,
+ GS_ODRS_PROVIDER_ERROR_NO_NETWORK)) {
+ g_autoptr(GsPluginEvent) event = NULL;
+
+ event = gs_plugin_event_new ("error", local_error,
+ "action", GS_PLUGIN_ACTION_DOWNLOAD,
+ NULL);
+
+ if (gs_plugin_job_get_interactive (helper->plugin_job))
+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INTERACTIVE);
+ else
+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING);
+ gs_plugin_loader_add_event (plugin_loader, event);
+
+ return TRUE;
+ }
+
+ g_propagate_error (error, g_steal_pointer (&local_error));
+
return FALSE;
+ }
}
#ifdef HAVE_SYSPROF
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]