[gnome-software: 15/72] repos: Load updates of the repo data asynchronously in a worker thread
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software: 15/72] repos: Load updates of the repo data asynchronously in a worker thread
- Date: Wed, 15 Dec 2021 13:00:54 +0000 (UTC)
commit 29019944358d6035542cbdaaa7f74b74eb4cb4ac
Author: Philip Withnall <pwithnall endlessos org>
Date: Fri Oct 22 16:04:27 2021 +0100
repos: Load updates of the repo data asynchronously in a worker thread
Rather than loading the repo data during the first `refine()` vfunc call
after the directory has changed (and the file monitor has invalidated
it), load it in a worker thread *when* the directory changes.
This means that the refine vfunc is now non-blocking, which means it can
be implemented in the main thread. This reduces the plugin’s use of
worker threads from one for *every* refine operation, to one for every
time the repos directory is updated (which is expected to happen very
infrequently, if ever).
Signed-off-by: Philip Withnall <pwithnall endlessos org>
Helps: #1472
plugins/repos/gs-plugin-repos.c | 126 +++++++++++++++++++++++-----------------
1 file changed, 73 insertions(+), 53 deletions(-)
---
diff --git a/plugins/repos/gs-plugin-repos.c b/plugins/repos/gs-plugin-repos.c
index db11ec774..57cc75e75 100644
--- a/plugins/repos/gs-plugin-repos.c
+++ b/plugins/repos/gs-plugin-repos.c
@@ -36,9 +36,7 @@ struct _GsPluginRepos {
* main thread with a strong reference and no lock.
*
* @mutex must be held when getting a strong reference to them, or
- * replacing them.
- *
- * Their state is undefined if @valid is false. */
+ * replacing them. */
GHashTable *fns; /* origin : filename */
GHashTable *urls; /* origin : url */
@@ -46,7 +44,10 @@ struct _GsPluginRepos {
gchar *reposdir;
GMutex mutex;
- gboolean valid; /* (atomic) */
+
+ /* Used to cancel a pending update operation which is loading the repos
+ * data in a worker thread. */
+ GCancellable *update_cancellable; /* (nullable) (owned) */
};
G_DEFINE_TYPE (GsPluginRepos, gs_plugin_repos, GS_TYPE_PLUGIN)
@@ -79,6 +80,8 @@ gs_plugin_repos_dispose (GObject *object)
{
GsPluginRepos *self = GS_PLUGIN_REPOS (object);
+ g_cancellable_cancel (self->update_cancellable);
+ g_clear_object (&self->update_cancellable);
g_clear_pointer (&self->reposdir, g_free);
g_clear_pointer (&self->fns, g_hash_table_unref);
g_clear_pointer (&self->urls, g_hash_table_unref);
@@ -97,13 +100,11 @@ gs_plugin_repos_finalize (GObject *object)
G_OBJECT_CLASS (gs_plugin_repos_parent_class)->finalize (object);
}
-/* Run in multiple threads; will take the mutex */
+/* Run in a worker thread; will take the mutex */
static gboolean
-gs_plugin_repos_ensure_valid (GsPluginRepos *self,
- GHashTable **filenames_out,
- GHashTable **urls_out,
- GCancellable *cancellable,
- GError **error)
+gs_plugin_repos_load (GsPluginRepos *self,
+ GCancellable *cancellable,
+ GError **error)
{
g_autoptr(GDir) dir = NULL;
const gchar *fn;
@@ -111,16 +112,6 @@ gs_plugin_repos_ensure_valid (GsPluginRepos *self,
g_autoptr(GHashTable) new_urls = NULL;
g_autoptr(GMutexLocker) locker = NULL;
- /* Clear out args */
- if (filenames_out != NULL)
- *filenames_out = NULL;
- if (urls_out != NULL)
- *urls_out = NULL;
-
- /* already valid */
- if (g_atomic_int_get (&self->valid))
- goto out;
-
new_filenames = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
new_urls = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
@@ -185,22 +176,27 @@ gs_plugin_repos_ensure_valid (GsPluginRepos *self,
g_clear_pointer (&self->urls, g_hash_table_unref);
self->urls = g_steal_pointer (&new_urls);
- g_atomic_int_set (&self->valid, TRUE);
-
-out:
- if (locker == NULL)
- locker = g_mutex_locker_new (&self->mutex);
-
g_assert (self->fns != NULL && self->urls != NULL);
- if (filenames_out != NULL)
- *filenames_out = g_hash_table_ref (self->fns);
- if (urls_out != NULL)
- *urls_out = g_hash_table_ref (self->urls);
-
return TRUE;
}
+/* Run in a worker thread. */
+static void
+update_repos_thread_cb (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GsPluginRepos *self = GS_PLUGIN_REPOS (source_object);
+ g_autoptr(GError) local_error = NULL;
+
+ if (!gs_plugin_repos_load (self, cancellable, &local_error))
+ g_task_return_error (task, g_steal_pointer (&local_error));
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
/* Run in the main thread. */
static void
gs_plugin_repos_changed_cb (GFileMonitor *monitor,
@@ -210,24 +206,17 @@ gs_plugin_repos_changed_cb (GFileMonitor *monitor,
gpointer user_data)
{
GsPluginRepos *self = GS_PLUGIN_REPOS (user_data);
+ g_autoptr(GTask) task = NULL;
- g_atomic_int_set (&self->valid, FALSE);
-}
+ /* Cancel any pending updates and schedule a new update of the repo data
+ * in a worker thread. */
+ g_cancellable_cancel (self->update_cancellable);
+ g_clear_object (&self->update_cancellable);
+ self->update_cancellable = g_cancellable_new ();
-/* Run in a worker thread. */
-static void
-setup_thread_cb (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
-{
- GsPluginRepos *self = GS_PLUGIN_REPOS (source_object);
- g_autoptr(GError) local_error = NULL;
-
- if (!gs_plugin_repos_ensure_valid (self, NULL, NULL, cancellable, &local_error))
- g_task_return_error (task, g_steal_pointer (&local_error));
- else
- g_task_return_boolean (task, TRUE);
+ task = g_task_new (self, self->update_cancellable, NULL, NULL);
+ g_task_set_source_tag (task, gs_plugin_repos_changed_cb);
+ g_task_run_in_thread (task, update_repos_thread_cb);
}
static void
@@ -256,7 +245,7 @@ gs_plugin_repos_setup_async (GsPlugin *plugin,
G_CALLBACK (gs_plugin_repos_changed_cb), self);
/* Set up the repos at startup. */
- g_task_run_in_thread (task, setup_thread_cb);
+ g_task_run_in_thread (task, update_repos_thread_cb);
}
static gboolean
@@ -267,6 +256,32 @@ gs_plugin_repos_setup_finish (GsPlugin *plugin,
return g_task_propagate_boolean (G_TASK (result), error);
}
+static void
+gs_plugin_repos_shutdown_async (GsPlugin *plugin,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsPluginRepos *self = GS_PLUGIN_REPOS (plugin);
+ g_autoptr(GTask) task = NULL;
+
+ task = g_task_new (plugin, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_repos_shutdown_async);
+
+ /* Cancel any ongoing update operations. */
+ g_cancellable_cancel (self->update_cancellable);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+gs_plugin_repos_shutdown_finish (GsPlugin *plugin,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
static void
refine_app (GsApp *app,
GsPluginRefineFlags flags,
@@ -349,17 +364,20 @@ gs_plugin_refine (GsPlugin *plugin,
GsPluginRepos *self = GS_PLUGIN_REPOS (plugin);
g_autoptr(GHashTable) filenames = NULL; /* (element-type utf8 filename) mapping origin to filename */
g_autoptr(GHashTable) urls = NULL; /* (element-type utf8 utf8) mapping origin to URL */
+ g_autoptr(GMutexLocker) locker = NULL;
/* nothing to do here */
if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_HOSTNAME) == 0)
return TRUE;
- /* Ensure the object state is valid, and grab a reference to it so it
- * can be accessed without holding the lock, to keep the critical
- * section small. */
- if (!gs_plugin_repos_ensure_valid (self, &filenames, &urls, cancellable, error))
- return FALSE;
+ /* Grab a reference to the object’s state so it can be accessed without
+ * holding the lock throughout, to keep the critical section small. */
+ locker = g_mutex_locker_new (&self->mutex);
+ filenames = g_hash_table_ref (self->fns);
+ urls = g_hash_table_ref (self->urls);
+ g_clear_pointer (&locker, g_mutex_locker_free);
+ /* Update each of the apps */
for (guint i = 0; i < gs_app_list_length (list); i++) {
GsApp *app = gs_app_list_index (list, i);
@@ -380,6 +398,8 @@ gs_plugin_repos_class_init (GsPluginReposClass *klass)
plugin_class->setup_async = gs_plugin_repos_setup_async;
plugin_class->setup_finish = gs_plugin_repos_setup_finish;
+ plugin_class->shutdown_async = gs_plugin_repos_shutdown_async;
+ plugin_class->shutdown_finish = gs_plugin_repos_shutdown_finish;
}
GType
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]