[gnome-software] Do not emit ::updates_changed() from plugins when performing internal actions



commit eefa2aabc66e7489eb13de277bd83a83ac04717b
Author: Richard Hughes <richard hughsie com>
Date:   Tue Apr 19 21:13:51 2016 +0100

    Do not emit ::updates_changed() from plugins when performing internal actions
    
    We only want to send this when the change is not a plugin action. If we're
    watching a file for changes (for instance in the fedora-distro-upgrade plugin)
    we also need to wait a small time to allow for inotify to bubble up to us.

 src/gs-plugin-loader.c                         |  149 ++++++++++++++++++------
 src/gs-plugin.h                                |   11 ++
 src/plugins/gs-plugin-appstream.c              |    3 +-
 src/plugins/gs-plugin-fedora-distro-upgrades.c |   41 +++++++
 4 files changed, 169 insertions(+), 35 deletions(-)
---
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index c2dc21e..2bdb782 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -109,6 +109,85 @@ gs_plugin_loader_app_sort_cb (gconstpointer a, gconstpointer b)
 }
 
 /**
+ * gs_plugin_loader_action_start:
+ **/
+static void
+gs_plugin_loader_action_start (GsPluginLoader *plugin_loader,
+                              GsPlugin *plugin,
+                              gboolean exclusive)
+{
+       GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+       guint i;
+
+       /* lock plugin */
+       if (exclusive) {
+               g_rw_lock_writer_lock (&plugin->rwlock);
+               plugin->flags |= GS_PLUGIN_FLAGS_EXCLUSIVE;
+       } else {
+               g_rw_lock_reader_lock (&plugin->rwlock);
+       }
+
+       /* set plugin as SELF and all plugins as OTHER */
+       plugin->flags |= GS_PLUGIN_FLAGS_RUNNING_SELF;
+       for (i = 0; i < priv->plugins->len; i++) {
+               GsPlugin *plugin_tmp;
+               plugin_tmp = g_ptr_array_index (priv->plugins, i);
+               if (!plugin_tmp->enabled)
+                       continue;
+               plugin_tmp->flags |= GS_PLUGIN_FLAGS_RUNNING_OTHER;
+       }
+}
+
+/**
+ * gs_plugin_loader_action_delay_cb:
+ **/
+static gboolean
+gs_plugin_loader_action_delay_cb (gpointer user_data)
+{
+       GsPlugin *plugin = GS_PLUGIN (user_data);
+       g_debug ("plugin no longer recently active: %s", plugin->name);
+       plugin->flags &= ~GS_PLUGIN_FLAGS_RECENT;
+       plugin->timer_id = 0;
+       return FALSE;
+}
+
+/**
+ * gs_plugin_loader_action_stop:
+ **/
+static void
+gs_plugin_loader_action_stop (GsPluginLoader *plugin_loader, GsPlugin *plugin)
+{
+       GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+       guint i;
+
+       /* clear plugin as SELF and all plugins as OTHER */
+       plugin->flags &= ~GS_PLUGIN_FLAGS_RUNNING_SELF;
+       for (i = 0; i < priv->plugins->len; i++) {
+               GsPlugin *plugin_tmp;
+               plugin_tmp = g_ptr_array_index (priv->plugins, i);
+               if (!plugin_tmp->enabled)
+                       continue;
+               plugin_tmp->flags &= ~GS_PLUGIN_FLAGS_RUNNING_OTHER;
+       }
+
+       /* unlock plugin */
+       if (plugin->flags & GS_PLUGIN_FLAGS_EXCLUSIVE) {
+               g_rw_lock_writer_unlock (&plugin->rwlock);
+               plugin->flags &= ~GS_PLUGIN_FLAGS_EXCLUSIVE;
+       } else {
+               g_rw_lock_reader_unlock (&plugin->rwlock);
+       }
+
+       /* unset this flag after 5 seconds */
+       plugin->flags |= GS_PLUGIN_FLAGS_RECENT;
+       if (plugin->timer_id > 0)
+               g_source_remove (plugin->timer_id);
+       plugin->timer_id = g_timeout_add (5000,
+                                         gs_plugin_loader_action_delay_cb,
+                                         plugin);
+}
+
+/**
  * gs_plugin_loader_run_adopt:
  **/
 static void
@@ -132,9 +211,9 @@ gs_plugin_loader_run_adopt (GsPluginLoader *plugin_loader, GList *list)
                        GsApp *app = GS_APP (l->data);
                        if (gs_app_get_management_plugin (app) != NULL)
                                continue;
-                       g_rw_lock_reader_lock (&plugin->rwlock);
+                       gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                        adopt_app_func (plugin, app);
-                       g_rw_lock_reader_unlock (&plugin->rwlock);
+                       gs_plugin_loader_action_stop (plugin_loader, plugin);
                        if (gs_app_get_management_plugin (app) != NULL) {
                                g_debug ("%s adopted %s", plugin->name,
                                         gs_app_get_id (app));
@@ -211,10 +290,10 @@ gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
                /* run the batched plugin symbol then the per-app plugin */
                if (plugin_func != NULL) {
                        g_autoptr(GError) error_local = NULL;
-                       g_rw_lock_reader_lock (&plugin->rwlock);
+                       gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                        ret = plugin_func (plugin, list, flags,
                                           cancellable, &error_local);
-                       g_rw_lock_reader_unlock (&plugin->rwlock);
+                       gs_plugin_loader_action_stop (plugin_loader, plugin);
                        if (!ret) {
                                g_warning ("failed to call %s on %s: %s",
                                           function_name, plugin->name,
@@ -226,10 +305,10 @@ gs_plugin_loader_run_refine (GsPluginLoader *plugin_loader,
                        for (l = *list; l != NULL; l = l->next) {
                                g_autoptr(GError) error_local = NULL;
                                app = GS_APP (l->data);
-                               g_rw_lock_reader_lock (&plugin->rwlock);
+                               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                                ret = plugin_app_func (plugin, app, flags,
                                                       cancellable, &error_local);
-                               g_rw_lock_reader_unlock (&plugin->rwlock);
+                               gs_plugin_loader_action_stop (plugin_loader, plugin);
                                if (!ret) {
                                        g_warning ("failed to call %s on %s: %s",
                                                   function_name_app, plugin->name,
@@ -363,9 +442,9 @@ gs_plugin_loader_run_results (GsPluginLoader *plugin_loader,
                ptask2 = as_profile_start (priv->profile,
                                           "GsPlugin::%s(%s)",
                                           plugin->name, function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, &list, cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -674,9 +753,9 @@ gs_plugin_loader_run_action (GsPluginLoader *plugin_loader,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, app, cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -1571,10 +1650,10 @@ gs_plugin_loader_search_thread_cb (GTask *task,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, values, &state->list,
                                   cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -1732,10 +1811,10 @@ gs_plugin_loader_search_files_thread_cb (GTask *task,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, values, &state->list,
                                   cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -1894,10 +1973,10 @@ gs_plugin_loader_search_what_provides_thread_cb (GTask *task,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, values, &state->list,
                                   cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -2062,10 +2141,10 @@ gs_plugin_loader_get_categories_thread_cb (GTask *task,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, &state->list,
                                   cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -2202,10 +2281,10 @@ gs_plugin_loader_get_category_apps_thread_cb (GTask *task,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, state->category, &state->list,
                                   cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -2526,10 +2605,10 @@ gs_plugin_loader_review_action_thread_cb (GTask *task,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          state->function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, state->app, state->review,
                                   cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   state->function_name, plugin->name,
@@ -2918,9 +2997,9 @@ gs_plugin_loader_run (GsPluginLoader *plugin_loader, const gchar *function_name)
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                plugin_func (plugin);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
        }
 }
@@ -3323,9 +3402,9 @@ gs_plugin_loader_setup (GsPluginLoader *plugin_loader,
                                           "GsPlugin::%s(%s)",
                                           plugin->name,
                                           function_name);
-               g_rw_lock_writer_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, TRUE);
                ret = plugin_func (plugin, NULL, &error_local);
-               g_rw_lock_writer_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_debug ("disabling %s as setup failed: %s",
                                 plugin->name, error_local->message);
@@ -3364,6 +3443,8 @@ gs_plugin_loader_dump_state (GsPluginLoader *plugin_loader)
 static void
 gs_plugin_loader_plugin_free (GsPlugin *plugin)
 {
+       if (plugin->timer_id > 0)
+               g_source_remove (plugin->timer_id);
        g_free (plugin->priv);
        g_free (plugin->name);
        g_rw_lock_clear (&plugin->rwlock);
@@ -3624,9 +3705,9 @@ gs_plugin_loader_run_refresh (GsPluginLoader *plugin_loader,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_writer_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, TRUE);
                ret = plugin_func (plugin, cache_age, flags, cancellable, &error_local);
-               g_rw_lock_writer_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -3766,10 +3847,10 @@ gs_plugin_loader_filename_to_app_thread_cb (GTask *task,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, &state->list, state->filename,
                                   cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -3909,9 +3990,9 @@ gs_plugin_loader_update_thread_cb (GTask *task,
                                          "GsPlugin::%s(%s)",
                                          plugin->name,
                                          function_name);
-               g_rw_lock_reader_lock (&plugin->rwlock);
+               gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                ret = plugin_func (plugin, state->list, cancellable, &error_local);
-               g_rw_lock_reader_unlock (&plugin->rwlock);
+               gs_plugin_loader_action_stop (plugin_loader, plugin);
                if (!ret) {
                        g_warning ("failed to call %s on %s: %s",
                                   function_name, plugin->name,
@@ -3949,11 +4030,11 @@ gs_plugin_loader_update_thread_cb (GTask *task,
                                                  plugin->name,
                                                  function_name,
                                                  gs_app_get_id (app));
-                       g_rw_lock_reader_lock (&plugin->rwlock);
+                       gs_plugin_loader_action_start (plugin_loader, plugin, FALSE);
                        ret = plugin_app_func (plugin, app,
                                               cancellable,
                                               &error_local);
-                       g_rw_lock_reader_unlock (&plugin->rwlock);
+                       gs_plugin_loader_action_stop (plugin_loader, plugin);
                        if (!ret) {
                                g_warning ("failed to call %s on %s: %s",
                                           function_name, plugin->name,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 4484161..7304439 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -61,6 +61,15 @@ typedef void (*GsPluginUpdatesChanged)       (GsPlugin       *plugin,
 typedef gboolean (*GsPluginListFilter) (GsApp          *app,
                                         gpointer        user_data);
 
+typedef enum {
+       GS_PLUGIN_FLAGS_NONE            = 0,
+       GS_PLUGIN_FLAGS_RUNNING_SELF    = 1 << 0,
+       GS_PLUGIN_FLAGS_RUNNING_OTHER   = 1 << 1,
+       GS_PLUGIN_FLAGS_EXCLUSIVE       = 1 << 2,
+       GS_PLUGIN_FLAGS_RECENT          = 1 << 3,
+       GS_PLUGIN_FLAGS_LAST
+} GsPluginFlags;
+
 struct GsPlugin {
        GModule                 *module;
        gdouble                  priority;      /* largest number gets run first */
@@ -81,6 +90,8 @@ struct GsPlugin {
        SoupSession             *soup_session;
        GHashTable              *cache;
        GRWLock                  rwlock;
+       GsPluginFlags            flags;
+       guint                    timer_id;
 };
 
 typedef enum {
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index 7689cc3..5bd2ec5 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -63,7 +63,8 @@ gs_plugin_appstream_store_changed_cb (AsStore *store, GsPlugin *plugin)
 
        /* this is not strictly true, but it causes all the UI to be reloaded
         * which is what we really want */
-       gs_plugin_updates_changed (plugin);
+       if ((plugin->flags & GS_PLUGIN_FLAGS_RUNNING_OTHER) == 0)
+               gs_plugin_updates_changed (plugin);
 }
 
 /**
diff --git a/src/plugins/gs-plugin-fedora-distro-upgrades.c b/src/plugins/gs-plugin-fedora-distro-upgrades.c
index 11178de..15517fd 100644
--- a/src/plugins/gs-plugin-fedora-distro-upgrades.c
+++ b/src/plugins/gs-plugin-fedora-distro-upgrades.c
@@ -31,6 +31,7 @@
 
 struct GsPluginPrivate {
        gchar           *cachefn;
+       GFileMonitor    *cachefn_monitor;
        gchar           *os_name;
        guint64          os_version;
 };
@@ -65,11 +66,39 @@ gs_plugin_initialize (GsPlugin *plugin)
 void
 gs_plugin_destroy (GsPlugin *plugin)
 {
+       if (plugin->priv->cachefn_monitor != NULL)
+               g_object_unref (plugin->priv->cachefn_monitor);
        g_free (plugin->priv->os_name);
        g_free (plugin->priv->cachefn);
 }
 
 /**
+ * gs_plugin_fedora_distro_upgrades_changed_cb:
+ */
+static void
+gs_plugin_fedora_distro_upgrades_changed_cb (GFileMonitor *monitor,
+                                            GFile *file,
+                                            GFile *other_file,
+                                            GFileMonitorEvent event_type,
+                                            gpointer user_data)
+{
+       GsPlugin *plugin = GS_PLUGIN (user_data);
+
+       /* only reload the update list if the plugin is NOT running itself
+        * and the time since it ran is greater than 5 seconds (inotify FTW) */
+       if (plugin->flags & GS_PLUGIN_FLAGS_RUNNING_SELF) {
+               g_debug ("no notify as plugin %s active", plugin->name);
+               return;
+       }
+       if (plugin->flags & GS_PLUGIN_FLAGS_RECENT) {
+               g_debug ("no notify as plugin %s recently active", plugin->name);
+               return;
+       }
+       g_debug ("cache file changed, so reloading upgrades list");
+       gs_plugin_updates_changed (plugin);
+}
+
+/**
  * gs_plugin_setup:
  */
 gboolean
@@ -78,6 +107,7 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
        gchar *endptr = NULL;
        g_autofree gchar *cachedir = NULL;
        g_autofree gchar *verstr = NULL;
+       g_autoptr(GFile) file = NULL;
 
        /* create the cachedir */
        cachedir = gs_utils_get_cachedir ("upgrades", error);
@@ -85,6 +115,17 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
                return FALSE;
        plugin->priv->cachefn = g_build_filename (cachedir, "fedora.json", NULL);
 
+       /* watch this in case it is changed by the user */
+       file = g_file_new_for_path (plugin->priv->cachefn);
+       plugin->priv->cachefn_monitor = g_file_monitor (file,
+                                                       G_FILE_MONITOR_NONE,
+                                                       cancellable,
+                                                       error);
+       if (plugin->priv->cachefn_monitor == NULL)
+               return FALSE;
+       g_signal_connect (plugin->priv->cachefn_monitor, "changed",
+                         G_CALLBACK (gs_plugin_fedora_distro_upgrades_changed_cb), plugin);
+
        /* read os-release for the current versions */
        plugin->priv->os_name = gs_os_release_get_name (error);
        if (plugin->priv->os_name == NULL)


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